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/utils/debug.h"
     15 #include "android/utils/timezone.h"
     16 #include "android/utils/system.h"
     17 #include "sim_card.h"
     18 #include "sysdeps.h"
     19 #include <memory.h>
     20 #include <stdarg.h>
     21 #include <time.h>
     22 #include <assert.h>
     23 #include <stdio.h>
     24 #include "sms.h"
     25 #include "remote_call.h"
     26 
     27 #define  DEBUG  1
     28 
     29 #if  1
     30 #  define  D_ACTIVE  VERBOSE_CHECK(modem)
     31 #else
     32 #  define  D_ACTIVE  DEBUG
     33 #endif
     34 
     35 #if 1
     36 #  define  R_ACTIVE  VERBOSE_CHECK(radio)
     37 #else
     38 #  define  R_ACTIVE  DEBUG
     39 #endif
     40 
     41 #if DEBUG
     42 #  define  D(...)   do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
     43 #  define  R(...)   do { if (R_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
     44 #else
     45 #  define  D(...)   ((void)0)
     46 #  define  R(...)   ((void)0)
     47 #endif
     48 
     49 #define  CALL_DELAY_DIAL   1000
     50 #define  CALL_DELAY_ALERT  1000
     51 
     52 /* the Android GSM stack checks that the operator's name has changed
     53  * when roaming is on. If not, it will not update the Roaming status icon
     54  *
     55  * this means that we need to emulate two distinct operators:
     56  * - the first one for the 'home' registration state, must also correspond
     57  *   to the emulated user's IMEI
     58  *
     59  * - the second one for the 'roaming' registration state, must have a
     60  *   different name and MCC/MNC
     61  */
     62 
     63 #define  OPERATOR_HOME_INDEX 0
     64 #define  OPERATOR_HOME_MCC   310
     65 #define  OPERATOR_HOME_MNC   260
     66 #define  OPERATOR_HOME_NAME  "Android"
     67 #define  OPERATOR_HOME_MCCMNC  STRINGIFY(OPERATOR_HOME_MCC) \
     68                                STRINGIFY(OPERATOR_HOME_MNC)
     69 
     70 #define  OPERATOR_ROAMING_INDEX 1
     71 #define  OPERATOR_ROAMING_MCC   310
     72 #define  OPERATOR_ROAMING_MNC   295
     73 #define  OPERATOR_ROAMING_NAME  "TelKila"
     74 #define  OPERATOR_ROAMING_MCCMNC  STRINGIFY(OPERATOR_ROAMING_MCC) \
     75                                   STRINGIFY(OPERATOR_ROAMING_MNC)
     76 
     77 #if DEBUG
     78 static const char*  quote( const char*  line )
     79 {
     80     static char  temp[1024];
     81     const char*  hexdigits = "0123456789abcdef";
     82     char*        p = temp;
     83     int          c;
     84 
     85     while ((c = *line++) != 0) {
     86         c &= 255;
     87         if (c >= 32 && c < 127) {
     88             *p++ = c;
     89         }
     90         else if (c == '\r') {
     91             memcpy( p, "<CR>", 4 );
     92             p += 4;
     93         }
     94         else if (c == '\n') {
     95             memcpy( p, "<LF>", 4 );strcat( p, "<LF>" );
     96             p += 4;
     97         }
     98         else {
     99             p[0] = '\\';
    100             p[1] = 'x';
    101             p[2] = hexdigits[ (c) >> 4 ];
    102             p[3] = hexdigits[ (c) & 15 ];
    103             p += 4;
    104         }
    105     }
    106     *p = 0;
    107     return temp;
    108 }
    109 #endif
    110 
    111 extern AGprsNetworkType
    112 android_parse_network_type( const char*  speed )
    113 {
    114     const struct { const char* name; AGprsNetworkType  type; }  types[] = {
    115          { "gprs", A_GPRS_NETWORK_GPRS },
    116          { "edge", A_GPRS_NETWORK_EDGE },
    117          { "umts", A_GPRS_NETWORK_UMTS },
    118          { "hsdpa", A_GPRS_NETWORK_UMTS },  /* not handled yet by Android GSM framework */
    119          { "full", A_GPRS_NETWORK_UMTS },
    120          { NULL, 0 }
    121     };
    122     int  nn;
    123 
    124     for (nn = 0; types[nn].name; nn++) {
    125         if ( !strcmp(speed, types[nn].name) )
    126             return types[nn].type;
    127     }
    128     /* not found, be conservative */
    129     return A_GPRS_NETWORK_GPRS;
    130 }
    131 
    132 /* 'mode' for +CREG/+CGREG commands */
    133 typedef enum {
    134     A_REGISTRATION_UNSOL_DISABLED     = 0,
    135     A_REGISTRATION_UNSOL_ENABLED      = 1,
    136     A_REGISTRATION_UNSOL_ENABLED_FULL = 2
    137 } ARegistrationUnsolMode;
    138 
    139 /* Operator selection mode, see +COPS commands */
    140 typedef enum {
    141     A_SELECTION_AUTOMATIC,
    142     A_SELECTION_MANUAL,
    143     A_SELECTION_DEREGISTRATION,
    144     A_SELECTION_SET_FORMAT,
    145     A_SELECTION_MANUAL_AUTOMATIC
    146 } AOperatorSelection;
    147 
    148 /* Operator status, see +COPS commands */
    149 typedef enum {
    150     A_STATUS_UNKNOWN = 0,
    151     A_STATUS_AVAILABLE,
    152     A_STATUS_CURRENT,
    153     A_STATUS_DENIED
    154 } AOperatorStatus;
    155 
    156 typedef struct {
    157     AOperatorStatus  status;
    158     char             name[3][16];
    159 } AOperatorRec, *AOperator;
    160 
    161 typedef struct AVoiceCallRec {
    162     ACallRec    call;
    163     SysTimer    timer;
    164     AModem      modem;
    165     char        is_remote;
    166 } AVoiceCallRec, *AVoiceCall;
    167 
    168 #define  MAX_OPERATORS  4
    169 
    170 typedef enum {
    171     A_DATA_IP = 0,
    172     A_DATA_PPP
    173 } ADataType;
    174 
    175 #define  A_DATA_APN_SIZE  32
    176 
    177 typedef struct {
    178     int        id;
    179     int        active;
    180     ADataType  type;
    181     char       apn[ A_DATA_APN_SIZE ];
    182 
    183 } ADataContextRec, *ADataContext;
    184 
    185 /* the spec says that there can only be a max of 4 contexts */
    186 #define  MAX_DATA_CONTEXTS  4
    187 #define  MAX_CALLS          4
    188 
    189 #define  A_MODEM_SELF_SIZE   3
    190 
    191 typedef struct AModemRec_
    192 {
    193     /* Legacy support */
    194     char          supportsNetworkDataType;
    195 
    196     /* Radio state */
    197     ARadioState   radio_state;
    198     int           area_code;
    199     int           cell_id;
    200     int           base_port;
    201 
    202     /* SMS */
    203     int           wait_sms;
    204 
    205     /* SIM card */
    206     ASimCard      sim;
    207 
    208     /* voice and data network registration */
    209     ARegistrationUnsolMode   voice_mode;
    210     ARegistrationState       voice_state;
    211     ARegistrationUnsolMode   data_mode;
    212     ARegistrationState       data_state;
    213     AGprsNetworkType         data_network;
    214 
    215     /* operator names */
    216     AOperatorSelection  oper_selection_mode;
    217     ANameIndex          oper_name_index;
    218     int                 oper_index;
    219     int                 oper_count;
    220     AOperatorRec        operators[ MAX_OPERATORS ];
    221 
    222     /* data connection contexts */
    223     ADataContextRec     data_contexts[ MAX_DATA_CONTEXTS ];
    224 
    225     /* active calls */
    226     AVoiceCallRec       calls[ MAX_CALLS ];
    227     int                 call_count;
    228 
    229     /* unsolicited callback */  /* XXX: TODO: use this */
    230     AModemUnsolFunc     unsol_func;
    231     void*               unsol_opaque;
    232 
    233     SmsReceiver         sms_receiver;
    234 
    235     int                 out_size;
    236     char                out_buff[1024];
    237 
    238 } AModemRec;
    239 
    240 
    241 static void
    242 amodem_unsol( AModem  modem, const char* format, ... )
    243 {
    244     if (modem->unsol_func) {
    245         va_list  args;
    246         va_start(args, format);
    247         vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
    248         va_end(args);
    249 
    250         modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    251     }
    252 }
    253 
    254 void
    255 amodem_receive_sms( AModem  modem, SmsPDU  sms )
    256 {
    257 #define  SMS_UNSOL_HEADER  "+CMT: 0\r\n"
    258 
    259     if (modem->unsol_func) {
    260         int    len, max;
    261         char*  p;
    262 
    263         strcpy( modem->out_buff, SMS_UNSOL_HEADER );
    264         p   = modem->out_buff + (sizeof(SMS_UNSOL_HEADER)-1);
    265         max = sizeof(modem->out_buff) - 3 - (sizeof(SMS_UNSOL_HEADER)-1);
    266         len = smspdu_to_hex( sms, p, max );
    267         if (len > max) /* too long */
    268             return;
    269         p[len]   = '\r';
    270         p[len+1] = '\n';
    271         p[len+2] = 0;
    272 
    273         R( "SMS>> %s\n", p );
    274 
    275         modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    276     }
    277 }
    278 
    279 static const char*
    280 amodem_printf( AModem  modem, const char*  format, ... )
    281 {
    282     va_list  args;
    283     va_start(args, format);
    284     vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
    285     va_end(args);
    286 
    287     return modem->out_buff;
    288 }
    289 
    290 static void
    291 amodem_begin_line( AModem  modem )
    292 {
    293     modem->out_size = 0;
    294 }
    295 
    296 static void
    297 amodem_add_line( AModem  modem, const char*  format, ... )
    298 {
    299     va_list  args;
    300     va_start(args, format);
    301     modem->out_size += vsnprintf( modem->out_buff + modem->out_size,
    302                                   sizeof(modem->out_buff) - modem->out_size,
    303                                   format, args );
    304     va_end(args);
    305 }
    306 
    307 static const char*
    308 amodem_end_line( AModem  modem )
    309 {
    310     modem->out_buff[ modem->out_size ] = 0;
    311     return modem->out_buff;
    312 }
    313 
    314 static void
    315 amodem_reset( AModem  modem )
    316 {
    317     modem->radio_state = A_RADIO_STATE_OFF;
    318     modem->wait_sms    = 0;
    319 
    320     modem->oper_name_index     = 2;
    321     modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
    322     modem->oper_index          = 0;
    323     modem->oper_count          = 2;
    324 
    325     modem->area_code = -1;
    326     modem->cell_id   = -1;
    327 
    328     strcpy( modem->operators[0].name[0], OPERATOR_HOME_NAME );
    329     strcpy( modem->operators[0].name[1], OPERATOR_HOME_NAME );
    330     strcpy( modem->operators[0].name[2], OPERATOR_HOME_MCCMNC );
    331 
    332     modem->operators[0].status        = A_STATUS_AVAILABLE;
    333 
    334     strcpy( modem->operators[1].name[0], OPERATOR_ROAMING_NAME );
    335     strcpy( modem->operators[1].name[1], OPERATOR_ROAMING_NAME );
    336     strcpy( modem->operators[1].name[2], OPERATOR_ROAMING_MCCMNC );
    337 
    338     modem->operators[1].status        = A_STATUS_AVAILABLE;
    339 
    340     modem->voice_mode   = A_REGISTRATION_UNSOL_ENABLED_FULL;
    341     modem->voice_state  = A_REGISTRATION_HOME;
    342     modem->data_mode    = A_REGISTRATION_UNSOL_ENABLED_FULL;
    343     modem->data_state   = A_REGISTRATION_HOME;
    344     modem->data_network = A_GPRS_NETWORK_UMTS;
    345 }
    346 
    347 static AModemRec   _android_modem[1];
    348 
    349 AModem
    350 amodem_create( int  base_port, AModemUnsolFunc  unsol_func, void*  unsol_opaque )
    351 {
    352     AModem  modem = _android_modem;
    353 
    354     amodem_reset( modem );
    355     modem->supportsNetworkDataType = 1;
    356     modem->base_port    = base_port;
    357     modem->unsol_func   = unsol_func;
    358     modem->unsol_opaque = unsol_opaque;
    359 
    360     modem->sim = asimcard_create();
    361 
    362     return  modem;
    363 }
    364 
    365 void
    366 amodem_set_legacy( AModem  modem )
    367 {
    368     modem->supportsNetworkDataType = 0;
    369 }
    370 
    371 void
    372 amodem_destroy( AModem  modem )
    373 {
    374     asimcard_destroy( modem->sim );
    375     modem->sim = NULL;
    376 }
    377 
    378 
    379 static int
    380 amodem_has_network( AModem  modem )
    381 {
    382     return !(modem->radio_state == A_RADIO_STATE_OFF   ||
    383              modem->oper_index < 0                  ||
    384              modem->oper_index >= modem->oper_count ||
    385              modem->oper_selection_mode == A_SELECTION_DEREGISTRATION );
    386 }
    387 
    388 
    389 ARadioState
    390 amodem_get_radio_state( AModem modem )
    391 {
    392     return modem->radio_state;
    393 }
    394 
    395 void
    396 amodem_set_radio_state( AModem modem, ARadioState  state )
    397 {
    398     modem->radio_state = state;
    399 }
    400 
    401 ASimCard
    402 amodem_get_sim( AModem  modem )
    403 {
    404     return  modem->sim;
    405 }
    406 
    407 ARegistrationState
    408 amodem_get_voice_registration( AModem  modem )
    409 {
    410     return modem->voice_state;
    411 }
    412 
    413 void
    414 amodem_set_voice_registration( AModem  modem, ARegistrationState  state )
    415 {
    416     modem->voice_state = state;
    417 
    418     if (state == A_REGISTRATION_HOME)
    419         modem->oper_index = OPERATOR_HOME_INDEX;
    420     else if (state == A_REGISTRATION_ROAMING)
    421         modem->oper_index = OPERATOR_ROAMING_INDEX;
    422 
    423     switch (modem->voice_mode) {
    424         case A_REGISTRATION_UNSOL_ENABLED:
    425             amodem_unsol( modem, "+CREG: %d,%d\r",
    426                           modem->voice_mode, modem->voice_state );
    427             break;
    428 
    429         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    430             amodem_unsol( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"\r",
    431                           modem->voice_mode, modem->voice_state,
    432                           modem->area_code, modem->cell_id );
    433             break;
    434         default:
    435             ;
    436     }
    437 }
    438 
    439 ARegistrationState
    440 amodem_get_data_registration( AModem  modem )
    441 {
    442     return modem->data_state;
    443 }
    444 
    445 void
    446 amodem_set_data_registration( AModem  modem, ARegistrationState  state )
    447 {
    448     modem->data_state = state;
    449 
    450     switch (modem->data_mode) {
    451         case A_REGISTRATION_UNSOL_ENABLED:
    452             amodem_unsol( modem, "+CGREG: %d,%d\r",
    453                           modem->data_mode, modem->data_state );
    454             break;
    455 
    456         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    457             if (modem->supportsNetworkDataType)
    458                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"\r",
    459                             modem->data_mode, modem->data_state,
    460                             modem->area_code, modem->cell_id,
    461                             modem->data_network );
    462             else
    463                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"\r",
    464                             modem->data_mode, modem->data_state,
    465                             modem->area_code, modem->cell_id );
    466             break;
    467 
    468         default:
    469             ;
    470     }
    471 }
    472 
    473 void
    474 amodem_set_data_network_type( AModem  modem, AGprsNetworkType   type )
    475 {
    476     modem->data_network = type;
    477     amodem_set_data_registration( modem, modem->data_state );
    478 }
    479 
    480 int
    481 amodem_get_operator_name ( AModem  modem, ANameIndex  index, char*  buffer, int  buffer_size )
    482 {
    483     AOperator  oper;
    484     int        len;
    485 
    486     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    487          (unsigned)index > 2 )
    488         return 0;
    489 
    490     oper = modem->operators + modem->oper_index;
    491     len  = strlen(oper->name[index]) + 1;
    492 
    493     if (buffer_size > len)
    494         buffer_size = len;
    495 
    496     if (buffer_size > 0) {
    497         memcpy( buffer, oper->name[index], buffer_size-1 );
    498         buffer[buffer_size] = 0;
    499     }
    500     return len;
    501 }
    502 
    503 /* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */
    504 void
    505 amodem_set_operator_name( AModem  modem, ANameIndex  index, const char*  buffer, int  buffer_size )
    506 {
    507     AOperator  oper;
    508     int        avail;
    509 
    510     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    511          (unsigned)index > 2 )
    512         return;
    513 
    514     oper = modem->operators + modem->oper_index;
    515 
    516     avail = sizeof(oper->name[0]);
    517     if (buffer_size < 0)
    518         buffer_size = strlen(buffer);
    519     if (buffer_size > avail-1)
    520         buffer_size = avail-1;
    521     memcpy( oper->name[index], buffer, buffer_size );
    522     oper->name[index][buffer_size] = 0;
    523 }
    524 
    525 /** CALLS
    526  **/
    527 int
    528 amodem_get_call_count( AModem  modem )
    529 {
    530     return modem->call_count;
    531 }
    532 
    533 ACall
    534 amodem_get_call( AModem  modem, int  index )
    535 {
    536     if ((unsigned)index >= (unsigned)modem->call_count)
    537         return NULL;
    538 
    539     return &modem->calls[index].call;
    540 }
    541 
    542 static AVoiceCall
    543 amodem_alloc_call( AModem   modem )
    544 {
    545     AVoiceCall  call  = NULL;
    546     int         count = modem->call_count;
    547 
    548     if (count < MAX_CALLS) {
    549         int  id;
    550 
    551         /* find a valid id for this call */
    552         for (id = 0; id < modem->call_count; id++) {
    553             int  found = 0;
    554             int  nn;
    555             for (nn = 0; nn < count; nn++) {
    556                 if ( modem->calls[nn].call.id == (id+1) ) {
    557                     found = 1;
    558                     break;
    559                 }
    560             }
    561             if (!found)
    562                 break;
    563         }
    564         call          = modem->calls + count;
    565         call->call.id = id + 1;
    566         call->modem   = modem;
    567 
    568         modem->call_count += 1;
    569     }
    570     return call;
    571 }
    572 
    573 
    574 static void
    575 amodem_free_call( AModem  modem, AVoiceCall  call )
    576 {
    577     int  nn;
    578 
    579     if (call->timer) {
    580         sys_timer_destroy( call->timer );
    581         call->timer = NULL;
    582     }
    583 
    584     if (call->is_remote) {
    585         remote_call_cancel( call->call.number, modem->base_port );
    586         call->is_remote = 0;
    587     }
    588 
    589     for (nn = 0; nn < modem->call_count; nn++) {
    590         if ( modem->calls + nn == call )
    591             break;
    592     }
    593     assert( nn < modem->call_count );
    594 
    595     memmove( modem->calls + nn,
    596              modem->calls + nn + 1,
    597              (modem->call_count - 1 - nn)*sizeof(*call) );
    598 
    599     modem->call_count -= 1;
    600 }
    601 
    602 
    603 static AVoiceCall
    604 amodem_find_call( AModem  modem, int  id )
    605 {
    606     int  nn;
    607 
    608     for (nn = 0; nn < modem->call_count; nn++) {
    609         AVoiceCall call = modem->calls + nn;
    610         if (call->call.id == id)
    611             return call;
    612     }
    613     return NULL;
    614 }
    615 
    616 static void
    617 amodem_send_calls_update( AModem  modem )
    618 {
    619    /* despite its name, this really tells the system that the call
    620     * state has changed */
    621     amodem_unsol( modem, "RING\r" );
    622 }
    623 
    624 
    625 int
    626 amodem_add_inbound_call( AModem  modem, const char*  number )
    627 {
    628     AVoiceCall  vcall = amodem_alloc_call( modem );
    629     ACall       call  = &vcall->call;
    630     int         len;
    631 
    632     if (call == NULL)
    633         return -1;
    634 
    635     call->dir   = A_CALL_INBOUND;
    636     call->state = A_CALL_INCOMING;
    637     call->mode  = A_CALL_VOICE;
    638     call->multi = 0;
    639 
    640     vcall->is_remote = (remote_number_string_to_port(number) > 0);
    641 
    642     len  = strlen(number);
    643     if (len >= sizeof(call->number))
    644         len = sizeof(call->number)-1;
    645 
    646     memcpy( call->number, number, len );
    647     call->number[len] = 0;
    648 
    649     amodem_send_calls_update( modem );
    650     return 0;
    651 }
    652 
    653 ACall
    654 amodem_find_call_by_number( AModem  modem, const char*  number )
    655 {
    656     AVoiceCall  vcall = modem->calls;
    657     AVoiceCall  vend  = vcall + modem->call_count;
    658 
    659     if (!number)
    660         return NULL;
    661 
    662     for ( ; vcall < vend; vcall++ )
    663         if ( !strcmp(vcall->call.number, number) )
    664             return &vcall->call;
    665 
    666     return  NULL;
    667 }
    668 
    669 
    670 static void
    671 acall_set_state( AVoiceCall    call, ACallState  state )
    672 {
    673     if (state != call->call.state)
    674     {
    675         if (call->is_remote)
    676         {
    677             const char*  number = call->call.number;
    678             int          port   = call->modem->base_port;
    679 
    680             switch (state) {
    681                 case A_CALL_HELD:
    682                     remote_call_other( number, port, REMOTE_CALL_HOLD );
    683                     break;
    684 
    685                 case A_CALL_ACTIVE:
    686                     remote_call_other( number, port, REMOTE_CALL_ACCEPT );
    687                     break;
    688 
    689                 default: ;
    690             }
    691         }
    692         call->call.state = state;
    693     }
    694 }
    695 
    696 
    697 int
    698 amodem_update_call( AModem  modem, const char*  fromNumber, ACallState  state )
    699 {
    700     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, fromNumber);
    701 
    702     if (vcall == NULL)
    703         return -1;
    704 
    705     acall_set_state( vcall, state );
    706     amodem_send_calls_update(modem);
    707     return 0;
    708 }
    709 
    710 
    711 int
    712 amodem_disconnect_call( AModem  modem, const char*  number )
    713 {
    714     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, number);
    715 
    716     if (!vcall)
    717         return -1;
    718 
    719     amodem_free_call( modem, vcall );
    720     amodem_send_calls_update(modem);
    721     return 0;
    722 }
    723 
    724 /** COMMAND HANDLERS
    725  **/
    726 
    727 static const char*
    728 unknownCommand( const char*  cmd, AModem  modem )
    729 {
    730     modem=modem;
    731     fprintf(stderr, ">>> unknown command '%s'\n", cmd );
    732     return "ERROR: unknown command\r";
    733 }
    734 
    735 static const char*
    736 handleRadioPower( const char*  cmd, AModem  modem )
    737 {
    738     if ( !strcmp( cmd, "+CFUN=0" ) )
    739     {
    740         /* turn radio off */
    741         modem->radio_state = A_RADIO_STATE_OFF;
    742     }
    743     else if ( !strcmp( cmd, "+CFUN=1" ) )
    744     {
    745         /* turn radio on */
    746         modem->radio_state = A_RADIO_STATE_ON;
    747     }
    748     return NULL;
    749 }
    750 
    751 static const char*
    752 handleRadioPowerReq( const char*  cmd, AModem  modem )
    753 {
    754     if (modem->radio_state != A_RADIO_STATE_OFF)
    755         return "+CFUN=1";
    756     else
    757         return "+CFUN=0";
    758 }
    759 
    760 static const char*
    761 handleSIMStatusReq( const char*  cmd, AModem  modem )
    762 {
    763     const char*  answer = NULL;
    764 
    765     switch (asimcard_get_status(modem->sim)) {
    766         case A_SIM_STATUS_ABSENT:    answer = "+CPIN: ABSENT"; break;
    767         case A_SIM_STATUS_READY:     answer = "+CPIN: READY"; break;
    768         case A_SIM_STATUS_NOT_READY: answer = "+CMERROR: NOT READY"; break;
    769         case A_SIM_STATUS_PIN:       answer = "+CPIN: SIM PIN"; break;
    770         case A_SIM_STATUS_PUK:       answer = "+CPIN: SIM PUK"; break;
    771         case A_SIM_STATUS_NETWORK_PERSONALIZATION: answer = "+CPIN: PH-NET PIN"; break;
    772         default:
    773             answer = "ERROR: internal error";
    774     }
    775     return answer;
    776 }
    777 
    778 static const char*
    779 handleNetworkRegistration( const char*  cmd, AModem  modem )
    780 {
    781     if ( !memcmp( cmd, "+CREG", 5 ) ) {
    782         cmd += 5;
    783         if (cmd[0] == '?') {
    784             return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"",
    785                                   modem->voice_mode, modem->voice_state,
    786                                   modem->area_code, modem->cell_id );
    787         } else if (cmd[0] == '=') {
    788             switch (cmd[1]) {
    789                 case '0':
    790                     modem->voice_mode  = A_REGISTRATION_UNSOL_DISABLED;
    791                     break;
    792 
    793                 case '1':
    794                     modem->voice_mode  = A_REGISTRATION_UNSOL_ENABLED;
    795                     break;
    796 
    797                 case '2':
    798                     modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
    799                     break;
    800 
    801                 case '?':
    802                     return "+CREG: (0-2)";
    803 
    804                 default:
    805                     return "ERROR: BAD COMMAND";
    806             }
    807         } else {
    808             assert( 0 && "unreachable" );
    809         }
    810     } else if ( !memcmp( cmd, "+CGREG", 6 ) ) {
    811         cmd += 6;
    812         if (cmd[0] == '?') {
    813             if (modem->supportsNetworkDataType)
    814                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"",
    815                                     modem->data_mode, modem->data_state,
    816                                     modem->area_code, modem->cell_id,
    817                                     modem->data_network );
    818             else
    819                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"",
    820                                     modem->data_mode, modem->data_state,
    821                                     modem->area_code, modem->cell_id );
    822         } else if (cmd[0] == '=') {
    823             switch (cmd[1]) {
    824                 case '0':
    825                     modem->data_mode  = A_REGISTRATION_UNSOL_DISABLED;
    826                     break;
    827 
    828                 case '1':
    829                     modem->data_mode  = A_REGISTRATION_UNSOL_ENABLED;
    830                     break;
    831 
    832                 case '2':
    833                     modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
    834                     break;
    835 
    836                 case '?':
    837                     return "+CGREG: (0-2)";
    838 
    839                 default:
    840                     return "ERROR: BAD COMMAND";
    841             }
    842         } else {
    843             assert( 0 && "unreachable" );
    844         }
    845     }
    846     return NULL;
    847 }
    848 
    849 static const char*
    850 handleSetDialTone( const char*  cmd, AModem  modem )
    851 {
    852     /* XXX: TODO */
    853     return NULL;
    854 }
    855 
    856 static const char*
    857 handleDeleteSMSonSIM( const char*  cmd, AModem  modem )
    858 {
    859     /* XXX: TODO */
    860     return NULL;
    861 }
    862 
    863 static const char*
    864 handleSIM_IO( const char*  cmd, AModem  modem )
    865 {
    866     return asimcard_io( modem->sim, cmd );
    867 }
    868 
    869 
    870 static const char*
    871 handleOperatorSelection( const char*  cmd, AModem  modem )
    872 {
    873     assert( !memcmp( "+COPS", cmd, 5 ) );
    874     cmd += 5;
    875     if (cmd[0] == '?') { /* ask for current operator */
    876         AOperator  oper = &modem->operators[ modem->oper_index ];
    877 
    878         if ( !amodem_has_network( modem ) )
    879         {
    880             /* this error code means "no network" */
    881             return amodem_printf( modem, "+CME ERROR: 30" );
    882         }
    883 
    884         oper = &modem->operators[ modem->oper_index ];
    885 
    886         if ( modem->oper_name_index == 2 )
    887             return amodem_printf( modem, "+COPS: %d,2,%s",
    888                                   modem->oper_selection_mode,
    889                                   oper->name[2] );
    890 
    891         return amodem_printf( modem, "+COPS: %d,%d,\"%s\"",
    892                               modem->oper_selection_mode,
    893                               modem->oper_name_index,
    894                               oper->name[ modem->oper_name_index ] );
    895     }
    896     else if (cmd[0] == '=' && cmd[1] == '?') {  /* ask for all available operators */
    897         const char*  comma = "+COPS: ";
    898         int          nn;
    899         amodem_begin_line( modem );
    900         for (nn = 0; nn < modem->oper_count; nn++) {
    901             AOperator  oper = &modem->operators[nn];
    902             amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma,
    903                              oper->status, oper->name[0], oper->name[1], oper->name[2] );
    904             comma = ", ";
    905         }
    906         return amodem_end_line( modem );
    907     }
    908     else if (cmd[0] == '=') {
    909         switch (cmd[1]) {
    910             case '0':
    911                 modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
    912                 return NULL;
    913 
    914             case '1':
    915                 {
    916                     int  format, nn, len, found = -1;
    917 
    918                     if (cmd[2] != ',')
    919                         goto BadCommand;
    920                     format = cmd[3] - '0';
    921                     if ( (unsigned)format > 2 )
    922                         goto BadCommand;
    923                     if (cmd[4] != ',')
    924                         goto BadCommand;
    925                     cmd += 5;
    926                     len  = strlen(cmd);
    927                     if (*cmd == '"') {
    928                         cmd++;
    929                         len -= 2;
    930                     }
    931                     if (len <= 0)
    932                         goto BadCommand;
    933 
    934                     for (nn = 0; nn < modem->oper_count; nn++) {
    935                         AOperator    oper = modem->operators + nn;
    936                         char*        name = oper->name[ format ];
    937 
    938                         if ( !memcpy( name, cmd, len ) && name[len] == 0 ) {
    939                             found = nn;
    940                             break;
    941                         }
    942                     }
    943 
    944                     if (found < 0) {
    945                         /* Selection failed */
    946                         return "+CME ERROR: 529";
    947                     } else if (modem->operators[found].status == A_STATUS_DENIED) {
    948                         /* network not allowed */
    949                         return "+CME ERROR: 32";
    950                     }
    951                     modem->oper_index = found;
    952 
    953                     /* set the voice and data registration states to home or roaming
    954                      * depending on the operator index
    955                      */
    956                     if (found == OPERATOR_HOME_INDEX) {
    957                         modem->voice_state = A_REGISTRATION_HOME;
    958                         modem->data_state  = A_REGISTRATION_HOME;
    959                     } else if (found == OPERATOR_ROAMING_INDEX) {
    960                         modem->voice_state = A_REGISTRATION_ROAMING;
    961                         modem->data_state  = A_REGISTRATION_ROAMING;
    962                     }
    963                     return NULL;
    964                 }
    965 
    966             case '2':
    967                 modem->oper_selection_mode = A_SELECTION_DEREGISTRATION;
    968                 return NULL;
    969 
    970             case '3':
    971                 {
    972                     int format;
    973 
    974                     if (cmd[2] != ',')
    975                         goto BadCommand;
    976 
    977                     format = cmd[3] - '0';
    978                     if ( (unsigned)format > 2 )
    979                         goto BadCommand;
    980 
    981                     modem->oper_name_index = format;
    982                     return NULL;
    983                 }
    984             default:
    985                 ;
    986         }
    987     }
    988 BadCommand:
    989     return unknownCommand(cmd,modem);
    990 }
    991 
    992 static const char*
    993 handleRequestOperator( const char*  cmd, AModem  modem )
    994 {
    995     AOperator  oper;
    996     cmd=cmd;
    997 
    998     if ( !amodem_has_network(modem) )
    999         return "+CME ERROR: 30";
   1000 
   1001     oper = modem->operators + modem->oper_index;
   1002     modem->oper_name_index = 2;
   1003     return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r"
   1004                           "+COPS: 0,1,\"%s\"\r"
   1005                           "+COPS: 0,2,\"%s\"",
   1006                           oper->name[0], oper->name[1], oper->name[2] );
   1007 }
   1008 
   1009 static const char*
   1010 handleSendSMStoSIM( const char*  cmd, AModem  modem )
   1011 {
   1012     /* XXX: TODO */
   1013     return "ERROR: unimplemented";
   1014 }
   1015 
   1016 static const char*
   1017 handleSendSMS( const char*  cmd, AModem  modem )
   1018 {
   1019     modem->wait_sms = 1;
   1020     return "> ";
   1021 }
   1022 
   1023 #if 0
   1024 static void
   1025 sms_address_dump( SmsAddress  address, FILE*  out )
   1026 {
   1027     int  nn, len = address->len;
   1028 
   1029     if (address->toa == 0x91) {
   1030         fprintf( out, "+" );
   1031     }
   1032     for (nn = 0; nn < len; nn += 2)
   1033     {
   1034         static const char  dialdigits[16] = "0123456789*#,N%";
   1035         int  c = address->data[nn/2];
   1036 
   1037         fprintf( out, "%c", dialdigits[c & 0xf] );
   1038         if (nn+1 >= len)
   1039             break;
   1040 
   1041         fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] );
   1042     }
   1043 }
   1044 
   1045 static void
   1046 smspdu_dump( SmsPDU  pdu, FILE*  out )
   1047 {
   1048     SmsAddressRec    address;
   1049     unsigned char    temp[256];
   1050     int              len;
   1051 
   1052     if (pdu == NULL) {
   1053         fprintf( out, "SMS PDU is (null)\n" );
   1054         return;
   1055     }
   1056 
   1057     fprintf( out, "SMS PDU type:       " );
   1058     switch (smspdu_get_type(pdu)) {
   1059         case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break;
   1060         case SMS_PDU_SUBMIT:  fprintf(out, "SUBMIT"); break;
   1061         case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break;
   1062         default: fprintf(out, "UNKNOWN");
   1063     }
   1064     fprintf( out, "\n        sender:   " );
   1065     if (smspdu_get_sender_address(pdu, &address) < 0)
   1066         fprintf( out, "(N/A)" );
   1067     else
   1068         sms_address_dump(&address, out);
   1069     fprintf( out, "\n        receiver: " );
   1070     if (smspdu_get_receiver_address(pdu, &address) < 0)
   1071         fprintf(out, "(N/A)");
   1072     else
   1073         sms_address_dump(&address, out);
   1074     fprintf( out, "\n        text:     " );
   1075     len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 );
   1076     if (len > sizeof(temp)-1 )
   1077         len = sizeof(temp)-1;
   1078     fprintf( out, "'%.*s'\n", len, temp );
   1079 }
   1080 #endif
   1081 
   1082 static const char*
   1083 handleSendSMSText( const char*  cmd, AModem  modem )
   1084 {
   1085 #if 1
   1086     SmsAddressRec  address;
   1087     char           number[16];
   1088     int            numlen;
   1089     int            len = strlen(cmd);
   1090     SmsPDU         pdu;
   1091 
   1092     /* get rid of trailing escape */
   1093     if (len > 0 && cmd[len-1] == 0x1a)
   1094         len -= 1;
   1095 
   1096     pdu = smspdu_create_from_hex( cmd, len );
   1097     if (pdu == NULL) {
   1098         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1099         return "+CMS ERROR: INVALID SMS PDU";
   1100     }
   1101     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1102         D("%s: could not get SMS receiver address from '%s'\n",
   1103           __FUNCTION__, cmd);
   1104         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1105     }
   1106 
   1107     do {
   1108         int  index;
   1109 
   1110         numlen = sms_address_to_str( &address, number, sizeof(number) );
   1111         if (numlen > sizeof(number)-1)
   1112             break;
   1113 
   1114         number[numlen] = 0;
   1115         if ( remote_number_string_to_port( number ) < 0 )
   1116             break;
   1117 
   1118         if (modem->sms_receiver == NULL) {
   1119             modem->sms_receiver = sms_receiver_create();
   1120             if (modem->sms_receiver == NULL) {
   1121                 D( "%s: could not create SMS receiver\n", __FUNCTION__ );
   1122                 break;
   1123             }
   1124         }
   1125 
   1126         index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu );
   1127         if (index < 0) {
   1128             D( "%s: could not add submit PDU\n", __FUNCTION__ );
   1129             break;
   1130         }
   1131         /* the PDU is now owned by the receiver */
   1132         pdu = NULL;
   1133 
   1134         if (index > 0) {
   1135             SmsAddressRec  from[1];
   1136             char           temp[10];
   1137             SmsPDU*        deliver;
   1138             int            nn;
   1139 
   1140             sprintf( temp, "%d", modem->base_port );
   1141             sms_address_from_str( from, temp, strlen(temp) );
   1142 
   1143             deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from );
   1144             if (deliver == NULL) {
   1145                 D( "%s: could not create deliver PDUs for SMS index %d\n",
   1146                    __FUNCTION__, index );
   1147                 break;
   1148             }
   1149 
   1150             for (nn = 0; deliver[nn] != NULL; nn++) {
   1151                 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) {
   1152                     D( "%s: could not send SMS PDU to remote emulator\n",
   1153                        __FUNCTION__ );
   1154                     break;
   1155                 }
   1156             }
   1157 
   1158             smspdu_free_list(deliver);
   1159         }
   1160 
   1161     } while (0);
   1162 
   1163     if (pdu != NULL)
   1164         smspdu_free(pdu);
   1165 
   1166 #elif 1
   1167     SmsAddressRec  address;
   1168     char           number[16];
   1169     int            numlen;
   1170     int            len = strlen(cmd);
   1171     SmsPDU         pdu;
   1172 
   1173     /* get rid of trailing escape */
   1174     if (len > 0 && cmd[len-1] == 0x1a)
   1175         len -= 1;
   1176 
   1177     pdu = smspdu_create_from_hex( cmd, len );
   1178     if (pdu == NULL) {
   1179         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1180         return "+CMS ERROR: INVALID SMS PDU";
   1181     }
   1182     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1183         D("%s: could not get SMS receiver address from '%s'\n",
   1184           __FUNCTION__, cmd);
   1185         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1186     }
   1187     do {
   1188         numlen = sms_address_to_str( &address, number, sizeof(number) );
   1189         if (numlen > sizeof(number)-1)
   1190             break;
   1191 
   1192         number[numlen] = 0;
   1193         if ( remote_number_string_to_port( number ) < 0 )
   1194             break;
   1195 
   1196         if ( remote_call_sms( number, modem->base_port, pdu ) < 0 )
   1197         {
   1198             D("%s: could not send SMS PDU to remote emulator\n",
   1199               __FUNCTION__);
   1200             return "+CMS ERROR: NO EMULATOR RECEIVER";
   1201         }
   1202     } while (0);
   1203 #else
   1204     fprintf(stderr, "SMS<< %s\n", cmd);
   1205     SmsPDU  pdu = smspdu_create_from_hex( cmd, strlen(cmd) );
   1206     if (pdu == NULL) {
   1207         fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd);
   1208     } else {
   1209         smspdu_dump(pdu, stderr);
   1210     }
   1211 #endif
   1212     return "+CMGS: 0\rOK\r";
   1213 }
   1214 
   1215 static const char*
   1216 handleChangeOrEnterPIN( const char*  cmd, AModem  modem )
   1217 {
   1218     assert( !memcmp( cmd, "+CPIN=", 6 ) );
   1219     cmd += 6;
   1220 
   1221     switch (asimcard_get_status(modem->sim)) {
   1222         case A_SIM_STATUS_ABSENT:
   1223             return "+CME ERROR: SIM ABSENT";
   1224 
   1225         case A_SIM_STATUS_NOT_READY:
   1226             return "+CME ERROR: SIM NOT READY";
   1227 
   1228         case A_SIM_STATUS_READY:
   1229             /* this may be a request to change the PIN */
   1230             {
   1231                 if (strlen(cmd) == 9 && cmd[4] == ',') {
   1232                     char  pin[5];
   1233                     memcpy( pin, cmd, 4 ); pin[4] = 0;
   1234 
   1235                     if ( !asimcard_check_pin( modem->sim, pin ) )
   1236                         return "+CME ERROR: BAD PIN";
   1237 
   1238                     memcpy( pin, cmd+5, 4 );
   1239                     asimcard_set_pin( modem->sim, pin );
   1240                     return "+CPIN: READY";
   1241                 }
   1242             }
   1243             break;
   1244 
   1245         case A_SIM_STATUS_PIN:   /* waiting for PIN */
   1246             if ( asimcard_check_pin( modem->sim, cmd ) )
   1247                 return "+CPIN: READY";
   1248             else
   1249                 return "+CME ERROR: BAD PIN";
   1250 
   1251         case A_SIM_STATUS_PUK:
   1252             if (strlen(cmd) == 9 && cmd[4] == ',') {
   1253                 char  puk[5];
   1254                 memcpy( puk, cmd, 4 );
   1255                 puk[4] = 0;
   1256                 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) )
   1257                     return "+CPIN: READY";
   1258                 else
   1259                     return "+CME ERROR: BAD PUK";
   1260             }
   1261             return "+CME ERROR: BAD PUK";
   1262 
   1263         default:
   1264             return "+CPIN: PH-NET PIN";
   1265     }
   1266 
   1267     return "+CME ERROR: BAD FORMAT";
   1268 }
   1269 
   1270 
   1271 static const char*
   1272 handleListCurrentCalls( const char*  cmd, AModem  modem )
   1273 {
   1274     int  nn;
   1275     amodem_begin_line( modem );
   1276     for (nn = 0; nn < modem->call_count; nn++) {
   1277         AVoiceCall  vcall = modem->calls + nn;
   1278         ACall       call  = &vcall->call;
   1279         if (call->mode == A_CALL_VOICE)
   1280             amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
   1281                              call->id, call->dir, call->state, call->mode,
   1282                              call->multi, call->number, 129 );
   1283     }
   1284     return amodem_end_line( modem );
   1285 }
   1286 
   1287 /* retrieve the current time and zone in a format suitable
   1288  * for %CTZV: unsolicited message
   1289  *  "yy/mm/dd,hh:mm:ss(+/-)tz"
   1290  *   mm is 0-based
   1291  *   tz is in number of quarter-hours
   1292  *
   1293  * it seems reference-ril doesn't parse the comma (,) as anything else than a token
   1294  * separator, so use a column (:) instead, the Java parsing code won't see a difference
   1295  *
   1296  */
   1297 static const char*
   1298 handleEndOfInit( const char*  cmd, AModem  modem )
   1299 {
   1300     time_t       now = time(NULL);
   1301     struct tm    utc, local;
   1302     long         e_local, e_utc;
   1303     long         tzdiff;
   1304     char         tzname[64];
   1305 
   1306     tzset();
   1307 
   1308     utc   = *gmtime( &now );
   1309     local = *localtime( &now );
   1310 
   1311     e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday);
   1312     e_utc   = utc.tm_min   + 60*(utc.tm_hour   + 24*utc.tm_yday);
   1313 
   1314     if ( utc.tm_year < local.tm_year )
   1315         e_local += 24*60;
   1316     else if ( utc.tm_year > local.tm_year )
   1317         e_utc += 24*60;
   1318 
   1319     tzdiff = e_local - e_utc;  /* timezone offset in minutes */
   1320 
   1321    /* retrieve a zoneinfo-compatible name for the host timezone
   1322     */
   1323     {
   1324         char*  end = tzname + sizeof(tzname);
   1325         char*  p = bufprint_zoneinfo_timezone( tzname, end );
   1326         if (p >= end)
   1327             strcpy(tzname, "Unknown/Unknown");
   1328 
   1329         /* now replace every / in the timezone name by a "!"
   1330          * that's because the code that reads the CTZV line is
   1331          * dumb and treats a / as a field separator...
   1332          */
   1333         p = tzname;
   1334         while (1) {
   1335             p = strchr(p, '/');
   1336             if (p == NULL)
   1337                 break;
   1338             *p = '!';
   1339             p += 1;
   1340         }
   1341     }
   1342 
   1343    /* as a special extension, we append the name of the host's time zone to the
   1344     * string returned with %CTZ. the system should contain special code to detect
   1345     * and deal with this case (since it normally relied on the operator's country code
   1346     * which is hard to simulate on a general-purpose computer
   1347     */
   1348     return amodem_printf( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s",
   1349              (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec,
   1350              (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15,
   1351              (local.tm_isdst > 0),
   1352              tzname );
   1353 }
   1354 
   1355 
   1356 static const char*
   1357 handleListPDPContexts( const char*  cmd, AModem  modem )
   1358 {
   1359     int  nn;
   1360     assert( !memcmp( cmd, "+CGACT?", 7 ) );
   1361     amodem_begin_line( modem );
   1362     for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   1363         ADataContext  data = modem->data_contexts + nn;
   1364         if (!data->active)
   1365             continue;
   1366         amodem_add_line( modem, "+CGACT: %d,%d\r", data->id, data->active );
   1367     }
   1368     return amodem_end_line( modem );
   1369 }
   1370 
   1371 static const char*
   1372 handleDefinePDPContext( const char*  cmd, AModem  modem )
   1373 {
   1374     assert( !memcmp( cmd, "+CGDCONT=", 9 ) );
   1375     cmd += 9;
   1376     if (cmd[0] == '?') {
   1377         int  nn;
   1378         amodem_begin_line(modem);
   1379         for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   1380             ADataContext  data = modem->data_contexts + nn;
   1381             if (!data->active)
   1382                 continue;
   1383             amodem_add_line( modem, "+CGDCONT: %d,%s,\"%s\",,0,0\r\n",
   1384                              data->id,
   1385                              data->type == A_DATA_IP ? "IP" : "PPP",
   1386                              data->apn );
   1387         }
   1388         return amodem_end_line(modem);
   1389     } else {
   1390         /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */
   1391         int              id = cmd[0] - '1';
   1392         ADataType        type;
   1393         char             apn[32];
   1394         ADataContext     data;
   1395 
   1396         if ((unsigned)id > 3)
   1397             goto BadCommand;
   1398 
   1399         if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) {
   1400             type = A_DATA_IP;
   1401             cmd += 8;
   1402         } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) {
   1403             type = A_DATA_PPP;
   1404             cmd += 9;
   1405         } else
   1406             goto BadCommand;
   1407 
   1408         {
   1409             const char*  p = strchr( cmd, '"' );
   1410             int          len;
   1411             if (p == NULL)
   1412                 goto BadCommand;
   1413             len = (int)( p - cmd );
   1414             if (len > sizeof(apn)-1 )
   1415                 len = sizeof(apn)-1;
   1416             memcpy( apn, cmd, len );
   1417             apn[len] = 0;
   1418         }
   1419 
   1420         data = modem->data_contexts + id;
   1421 
   1422         data->id     = id + 1;
   1423         data->active = 1;
   1424         data->type   = type;
   1425         memcpy( data->apn, apn, sizeof(data->apn) );
   1426     }
   1427     return NULL;
   1428 BadCommand:
   1429     return "ERROR: BAD COMMAND";
   1430 }
   1431 
   1432 
   1433 static const char*
   1434 handleStartPDPContext( const char*  cmd, AModem  modem )
   1435 {
   1436     /* XXX: TODO: handle PDP start appropriately */
   1437     /* for the moment, always return success */
   1438 #if 0
   1439     AVoiceCall  vcall = amodem_alloc_call( modem );
   1440     ACall       call  = (ACall) vcall;
   1441     if (call == NULL) {
   1442         return "ERROR: TOO MANY CALLS";
   1443     }
   1444     call->id    = 1;
   1445     call->dir   = A_CALL_OUTBOUND;
   1446     /* XXX: it would be better to delay this */
   1447     call->state = A_CALL_ACTIVE;
   1448     call->mode  = A_CALL_DATA;
   1449     call->multi = 0;
   1450     strcpy( call->number, "012345" );
   1451 #endif
   1452     return NULL;
   1453 }
   1454 
   1455 
   1456 static void
   1457 remote_voice_call_event( void*  _vcall, int  success )
   1458 {
   1459     AVoiceCall  vcall = _vcall;
   1460     AModem      modem = vcall->modem;
   1461 
   1462     /* NOTE: success only means we could send the "gsm in new" command
   1463      * to the remote emulator, nothing more */
   1464 
   1465     if (!success) {
   1466         /* aargh, the remote emulator probably quitted at that point */
   1467         amodem_free_call(modem, vcall);
   1468         amodem_send_calls_update(modem);
   1469     }
   1470 }
   1471 
   1472 
   1473 static void
   1474 voice_call_event( void*  _vcall )
   1475 {
   1476     AVoiceCall  vcall = _vcall;
   1477     ACall       call  = &vcall->call;
   1478 
   1479     switch (call->state) {
   1480         case A_CALL_DIALING:
   1481             call->state = A_CALL_ALERTING;
   1482 
   1483             if (vcall->is_remote) {
   1484                 if ( remote_call_dial( call->number,
   1485                                        vcall->modem->base_port,
   1486                                        remote_voice_call_event, vcall ) < 0 )
   1487                 {
   1488                    /* we could not connect, probably because the corresponding
   1489                     * emulator is not running, so simply destroy this call.
   1490                     * XXX: should we send some sort of message to indicate BAD NUMBER ? */
   1491                     /* it seems the Android code simply waits for changes in the list   */
   1492                     amodem_free_call( vcall->modem, vcall );
   1493                 }
   1494             } else {
   1495                /* this is not a remote emulator number, so just simulate
   1496                 * a small ringing delay */
   1497                 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT,
   1498                                voice_call_event, vcall );
   1499             }
   1500             break;
   1501 
   1502         case A_CALL_ALERTING:
   1503             call->state = A_CALL_ACTIVE;
   1504             break;
   1505 
   1506         default:
   1507             assert( 0 && "unreachable event call state" );
   1508     }
   1509     amodem_send_calls_update(vcall->modem);
   1510 }
   1511 
   1512 
   1513 static const char*
   1514 handleDial( const char*  cmd, AModem  modem )
   1515 {
   1516     AVoiceCall  vcall = amodem_alloc_call( modem );
   1517     ACall       call  = &vcall->call;
   1518     int         len;
   1519 
   1520     if (call == NULL)
   1521         return "ERROR: TOO MANY CALLS";
   1522 
   1523     assert( cmd[0] == 'D' );
   1524     call->dir   = A_CALL_OUTBOUND;
   1525     call->state = A_CALL_DIALING;
   1526     call->mode  = A_CALL_VOICE;
   1527     call->multi = 0;
   1528 
   1529     cmd += 1;
   1530     len  = strlen(cmd);
   1531     if (len > 0 && cmd[len-1] == ';')
   1532         len--;
   1533     if (len >= sizeof(call->number))
   1534         len = sizeof(call->number)-1;
   1535 
   1536     memcpy( call->number, cmd, len );
   1537     call->number[len] = 0;
   1538 
   1539     vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
   1540 
   1541     vcall->timer = sys_timer_create();
   1542     sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
   1543                    voice_call_event, vcall );
   1544 
   1545     return NULL;
   1546 }
   1547 
   1548 
   1549 static const char*
   1550 handleAnswer( const char*  cmd, AModem  modem )
   1551 {
   1552     int  nn;
   1553     for (nn = 0; nn < modem->call_count; nn++) {
   1554         AVoiceCall  vcall = modem->calls + nn;
   1555         ACall       call  = &vcall->call;
   1556 
   1557         if (cmd[0] == 'A') {
   1558             if (call->state == A_CALL_INCOMING) {
   1559                 acall_set_state( vcall, A_CALL_ACTIVE );
   1560             }
   1561             else if (call->state == A_CALL_ACTIVE) {
   1562                 acall_set_state( vcall, A_CALL_HELD );
   1563             }
   1564         } else if (cmd[0] == 'H') {
   1565             /* ATH: hangup, since user is busy */
   1566             if (call->state == A_CALL_INCOMING) {
   1567                 amodem_free_call( modem, vcall );
   1568                 break;
   1569             }
   1570         }
   1571     }
   1572     return NULL;
   1573 }
   1574 
   1575 static const char*
   1576 handleHangup( const char*  cmd, AModem  modem )
   1577 {
   1578     if ( !memcmp(cmd, "+CHLD=", 6) ) {
   1579         int  nn;
   1580         cmd += 6;
   1581         switch (cmd[0]) {
   1582             case '0':  /* release all held, and set busy for waiting calls */
   1583                 for (nn = 0; nn < modem->call_count; nn++) {
   1584                     AVoiceCall  vcall = modem->calls + nn;
   1585                     ACall       call  = &vcall->call;
   1586                     if (call->mode != A_CALL_VOICE)
   1587                         continue;
   1588                     if (call->state == A_CALL_HELD    ||
   1589                         call->state == A_CALL_WAITING ||
   1590                         call->state == A_CALL_INCOMING) {
   1591                         amodem_free_call(modem, vcall);
   1592                         nn--;
   1593                     }
   1594                 }
   1595                 break;
   1596 
   1597             case '1':
   1598                 if (cmd[1] == 0) { /* release all active, accept held one */
   1599                     for (nn = 0; nn < modem->call_count; nn++) {
   1600                         AVoiceCall  vcall = modem->calls + nn;
   1601                         ACall       call  = &vcall->call;
   1602                         if (call->mode != A_CALL_VOICE)
   1603                             continue;
   1604                         if (call->state == A_CALL_ACTIVE) {
   1605                             amodem_free_call(modem, vcall);
   1606                             nn--;
   1607                         }
   1608                         else if (call->state == A_CALL_HELD     ||
   1609                                  call->state == A_CALL_WAITING) {
   1610                             acall_set_state( vcall, A_CALL_ACTIVE );
   1611                         }
   1612                     }
   1613                 } else {  /* release specific call */
   1614                     int  id = cmd[1] - '0';
   1615                     AVoiceCall  vcall = amodem_find_call( modem, id );
   1616                     if (vcall != NULL)
   1617                         amodem_free_call( modem, vcall );
   1618                 }
   1619                 break;
   1620 
   1621             case '2':
   1622                 if (cmd[1] == 0) {  /* place all active on hold, accept held or waiting one */
   1623                     for (nn = 0; nn < modem->call_count; nn++) {
   1624                         AVoiceCall  vcall = modem->calls + nn;
   1625                         ACall       call  = &vcall->call;
   1626                         if (call->mode != A_CALL_VOICE)
   1627                             continue;
   1628                         if (call->state == A_CALL_ACTIVE) {
   1629                             acall_set_state( vcall, A_CALL_HELD );
   1630                         }
   1631                         else if (call->state == A_CALL_HELD     ||
   1632                                  call->state == A_CALL_WAITING) {
   1633                             acall_set_state( vcall, A_CALL_ACTIVE );
   1634                         }
   1635                     }
   1636                 } else {  /* place all active on hold, except a specific one */
   1637                     int   id = cmd[1] - '0';
   1638                     for (nn = 0; nn < modem->call_count; nn++) {
   1639                         AVoiceCall  vcall = modem->calls + nn;
   1640                         ACall       call  = &vcall->call;
   1641                         if (call->mode != A_CALL_VOICE)
   1642                             continue;
   1643                         if (call->state == A_CALL_ACTIVE && call->id != id) {
   1644                             acall_set_state( vcall, A_CALL_HELD );
   1645                         }
   1646                     }
   1647                 }
   1648                 break;
   1649 
   1650             case '3':  /* add a held call to the conversation */
   1651                 for (nn = 0; nn < modem->call_count; nn++) {
   1652                     AVoiceCall  vcall = modem->calls + nn;
   1653                     ACall       call  = &vcall->call;
   1654                     if (call->mode != A_CALL_VOICE)
   1655                         continue;
   1656                     if (call->state == A_CALL_HELD) {
   1657                         acall_set_state( vcall, A_CALL_ACTIVE );
   1658                         break;
   1659                     }
   1660                 }
   1661                 break;
   1662 
   1663             case '4':  /* connect the two calls */
   1664                 for (nn = 0; nn < modem->call_count; nn++) {
   1665                     AVoiceCall  vcall = modem->calls + nn;
   1666                     ACall       call  = &vcall->call;
   1667                     if (call->mode != A_CALL_VOICE)
   1668                         continue;
   1669                     if (call->state == A_CALL_HELD) {
   1670                         acall_set_state( vcall, A_CALL_ACTIVE );
   1671                         break;
   1672                     }
   1673                 }
   1674                 break;
   1675         }
   1676     }
   1677     else
   1678         return "ERROR: BAD COMMAND";
   1679 
   1680     return NULL;
   1681 }
   1682 
   1683 
   1684 /* a function used to deal with a non-trivial request */
   1685 typedef const char*  (*ResponseHandler)(const char*  cmd, AModem  modem);
   1686 
   1687 static const struct {
   1688     const char*      cmd;     /* command coming from libreference-ril.so, if first
   1689                                  character is '!', then the rest is a prefix only */
   1690 
   1691     const char*      answer;  /* default answer, NULL if needs specific handling or
   1692                                  if OK is good enough */
   1693 
   1694     ResponseHandler  handler; /* specific handler, ignored if 'answer' is not NULL,
   1695                                  NULL if OK is good enough */
   1696 } sDefaultResponses[] =
   1697 {
   1698     /* see onRadioPowerOn() */
   1699     { "%CPHS=1", NULL, NULL },
   1700     { "%CTZV=1", NULL, NULL },
   1701 
   1702     /* see onSIMReady() */
   1703     { "+CSMS=1", "+CSMS: 1, 1, 1", NULL },
   1704     { "+CNMI=1,2,2,1,1", NULL, NULL },
   1705 
   1706     /* see requestRadioPower() */
   1707     { "+CFUN=0", NULL, handleRadioPower },
   1708     { "+CFUN=1", NULL, handleRadioPower },
   1709 
   1710     /* see requestOrSendPDPContextList() */
   1711     { "+CGACT?", "", handleListPDPContexts },
   1712 
   1713     /* see requestOperator() */
   1714     { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator },
   1715 
   1716     /* see requestQueryNetworkSelectionMode() */
   1717     { "!+COPS", NULL, handleOperatorSelection },
   1718 
   1719     /* see requestGetCurrentCalls() */
   1720     { "+CLCC", NULL, handleListCurrentCalls },
   1721 
   1722     /* see requestWriteSmsToSim() */
   1723     { "!+CMGW=", NULL, handleSendSMStoSIM },
   1724 
   1725     /* see requestHangup() */
   1726     { "!+CHLD=", NULL, handleHangup },
   1727 
   1728     /* see requestSignalStrength() */
   1729     { "+CSQ", "+CSQ: 7,99", NULL },  /* XXX: TODO: implement variable signal strength and error rates */
   1730 
   1731     /* see requestRegistrationState() */
   1732     { "!+CREG", NULL, handleNetworkRegistration },
   1733     { "!+CGREG", NULL, handleNetworkRegistration },
   1734 
   1735     /* see requestSendSMS() */
   1736     { "!+CMGS=", NULL, handleSendSMS },
   1737 
   1738     /* see requestSetupDefaultPDP() */
   1739     { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL },
   1740     { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL },
   1741 
   1742     { "!+CGDCONT=", NULL, handleDefinePDPContext },
   1743 
   1744     { "+CGQREQ=1", NULL, NULL },
   1745     { "+CGQMIN=1", NULL, NULL },
   1746     { "+CGEREP=1,0", NULL, NULL },
   1747     { "+CGACT=1,0", NULL, NULL },
   1748     { "D*99***1#", NULL, handleStartPDPContext },
   1749 
   1750     /* see requestDial() */
   1751     { "!D", NULL, handleDial },  /* the code says that success/error is ignored, the call state will
   1752                               be polled through +CLCC instead */
   1753 
   1754     /* see requestSMSAcknowledge() */
   1755     { "+CNMA=1", NULL, NULL },
   1756     { "+CNMA=2", NULL, NULL },
   1757 
   1758     /* see requestSIM_IO() */
   1759     { "!+CRSM=", NULL, handleSIM_IO },
   1760 
   1761     /* see onRequest() */
   1762     { "+CHLD=0", NULL, handleHangup },
   1763     { "+CHLD=1", NULL, handleHangup },
   1764     { "+CHLD=2", NULL, handleHangup },
   1765     { "+CHLD=3", NULL, handleHangup },
   1766     { "A", NULL, handleAnswer },  /* answer the call */
   1767     { "H", NULL, handleAnswer },  /* user is busy */
   1768     { "!+VTS=", NULL, handleSetDialTone },
   1769     { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL },   /* request internation subscriber identification number */
   1770     { "+CGSN", "000000000000000", NULL },   /* request model version */
   1771     { "+CUSD=2",NULL, NULL }, /* Cancel USSD */
   1772     { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */
   1773     { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */
   1774     { "!+CPIN=", NULL, handleChangeOrEnterPIN },
   1775 
   1776     /* see getSIMStatus() */
   1777     { "+CPIN?", NULL, handleSIMStatusReq },
   1778     { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL },
   1779 
   1780     /* see isRadioOn() */
   1781     { "+CFUN?", NULL, handleRadioPowerReq },
   1782 
   1783     /* see initializeCallback() */
   1784     { "E0Q0V1", NULL, NULL },
   1785     { "S0=0", NULL, NULL },
   1786     { "+CMEE=1", NULL, NULL },
   1787     { "+CREG=2", NULL, handleNetworkRegistration },
   1788     { "+CREG=1", NULL, handleNetworkRegistration },
   1789     { "+CGREG=1", NULL, handleNetworkRegistration },
   1790     { "+CCWA=1", NULL, NULL },
   1791     { "+CMOD=0", NULL, NULL },
   1792     { "+CMUT=0", NULL, NULL },
   1793     { "+CSSN=0,1", NULL, NULL },
   1794     { "+COLP=0", NULL, NULL },
   1795     { "+CSCS=\"HEX\"", NULL, NULL },
   1796     { "+CUSD=1", NULL, NULL },
   1797     { "+CGEREP=1,0", NULL, NULL },
   1798     { "+CMGF=0", NULL, handleEndOfInit },  /* now is a goof time to send the current tme and timezone */
   1799     { "%CPI=3", NULL, NULL },
   1800     { "%CSTAT=1", NULL, NULL },
   1801 
   1802     /* end of list */
   1803     {NULL, NULL, NULL}
   1804 };
   1805 
   1806 
   1807 #define  REPLY(str)  do { const char*  s = (str); R(">> %s\n", quote(s)); return s; } while (0)
   1808 
   1809 const char*  amodem_send( AModem  modem, const char*  cmd )
   1810 {
   1811     const char*  answer;
   1812 
   1813     if ( modem->wait_sms != 0 ) {
   1814         modem->wait_sms = 0;
   1815         R( "SMS<< %s\n", quote(cmd) );
   1816         answer = handleSendSMSText( cmd, modem );
   1817         REPLY(answer);
   1818     }
   1819 
   1820     /* everything that doesn't start with 'AT' is not a command, right ? */
   1821     if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) {
   1822         /* R( "-- %s\n", quote(cmd) ); */
   1823         return NULL;
   1824     }
   1825     R( "<< %s\n", quote(cmd) );
   1826 
   1827     cmd += 2;
   1828 
   1829     /* TODO: implement command handling */
   1830     {
   1831         int  nn, found = 0;
   1832 
   1833         for (nn = 0; ; nn++) {
   1834             const char*  scmd = sDefaultResponses[nn].cmd;
   1835 
   1836             if (!scmd) /* end of list */
   1837                 break;
   1838 
   1839             if (scmd[0] == '!') { /* prefix match */
   1840                 int  len = strlen(++scmd);
   1841 
   1842                 if ( !memcmp( scmd, cmd, len ) ) {
   1843                     found = 1;
   1844                     break;
   1845                 }
   1846             } else { /* full match */
   1847                 if ( !strcmp( scmd, cmd ) ) {
   1848                     found = 1;
   1849                     break;
   1850                 }
   1851             }
   1852         }
   1853 
   1854         if ( !found )
   1855         {
   1856             D( "** UNSUPPORTED COMMAND **\n" );
   1857             REPLY( "ERROR: UNSUPPORTED" );
   1858         }
   1859         else
   1860         {
   1861             const char*      answer  = sDefaultResponses[nn].answer;
   1862             ResponseHandler  handler = sDefaultResponses[nn].handler;
   1863 
   1864             if ( answer != NULL ) {
   1865                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   1866             }
   1867 
   1868             if (handler == NULL) {
   1869                 REPLY( "OK" );
   1870             }
   1871 
   1872             answer = handler( cmd, modem );
   1873             if (answer == NULL)
   1874                 REPLY( "OK" );
   1875 
   1876             if ( !memcmp( answer, "> ", 2 )     ||
   1877                  !memcmp( answer, "ERROR", 5 )  ||
   1878                  !memcmp( answer, "+CME ERROR", 6 ) )
   1879             {
   1880                 REPLY( answer );
   1881             }
   1882 
   1883             if (answer != modem->out_buff)
   1884                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   1885 
   1886             strcat( modem->out_buff, "\rOK" );
   1887             REPLY( answer );
   1888         }
   1889     }
   1890 }
   1891