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
84static const char* _amodem_switch_technology(AModem modem, AModemTech newtech, int32_t newpreferred);
85static int _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss);
86static int _amodem_set_cdma_prl_version( AModem modem, int prlVersion);
87
88#if DEBUG
89static 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
122extern AModemTech
123android_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
143extern ADataNetworkType
144android_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 */
168typedef 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 */
175typedef 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 */
184typedef enum {
185    A_STATUS_UNKNOWN = 0,
186    A_STATUS_AVAILABLE,
187    A_STATUS_CURRENT,
188    A_STATUS_DENIED
189} AOperatorStatus;
190
191typedef struct {
192    AOperatorStatus  status;
193    char             name[3][16];
194} AOperatorRec, *AOperator;
195
196typedef 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
205typedef enum {
206    A_DATA_IP = 0,
207    A_DATA_PPP
208} ADataType;
209
210#define  A_DATA_APN_SIZE  32
211
212typedef 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
229typedef 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
304static void
305amodem_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
317void
318amodem_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
342static const char*
343amodem_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
353static void
354amodem_begin_line( AModem  modem )
355{
356    modem->out_size = 0;
357}
358
359static void
360amodem_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
370static const char*
371amodem_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
392static AConfig *
393amodem_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
405static int
406amodem_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
424const char *
425amodem_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
442static 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
453static 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
464static void
465amodem_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
523static AVoiceCall amodem_alloc_call( AModem   modem );
524static void amodem_free_call( AModem  modem, AVoiceCall  call );
525
526#define MODEM_DEV_STATE_SAVE_VERSION 1
527
528static 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
549static 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
576static AModemRec   _android_modem[1];
577
578AModem
579amodem_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
607void
608amodem_set_legacy( AModem  modem )
609{
610    modem->supportsNetworkDataType = 0;
611}
612
613void
614amodem_destroy( AModem  modem )
615{
616    asimcard_destroy( modem->sim );
617    modem->sim = NULL;
618}
619
620
621static int
622amodem_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
631ARadioState
632amodem_get_radio_state( AModem modem )
633{
634    return modem->radio_state;
635}
636
637void
638amodem_set_radio_state( AModem modem, ARadioState  state )
639{
640    modem->radio_state = state;
641}
642
643ASimCard
644amodem_get_sim( AModem  modem )
645{
646    return  modem->sim;
647}
648
649ARegistrationState
650amodem_get_voice_registration( AModem  modem )
651{
652    return modem->voice_state;
653}
654
655void
656amodem_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
681ARegistrationState
682amodem_get_data_registration( AModem  modem )
683{
684    return modem->data_state;
685}
686
687void
688amodem_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
715static int
716amodem_nvram_set( AModem modem, const char *name, const char *value )
717{
718    aconfig_set(modem->nvram_config, name, value);
719    return 0;
720}
721static AModemTech
722tech_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
741void
742amodem_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
755int
756amodem_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 */
779void
780amodem_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 **/
802int
803amodem_get_call_count( AModem  modem )
804{
805    return modem->call_count;
806}
807
808ACall
809amodem_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
817static AVoiceCall
818amodem_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
849static void
850amodem_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
878static AVoiceCall
879amodem_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
891static void
892amodem_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
900int
901amodem_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
928ACall
929amodem_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
944void
945amodem_set_signal_strength( AModem modem, int rssi, int ber )
946{
947    modem->rssi = rssi;
948    modem->ber = ber;
949}
950
951static void
952acall_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
978int
979amodem_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
992int
993amodem_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
1008static const char*
1009unknownCommand( 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 */
1024static 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
1037static AModemTech
1038chooseTechFromMask( 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
1058static 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
1086static int
1087parsePreferred( 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
1104void
1105amodem_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
1113static 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
1124void
1125amodem_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
1134static 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
1149static const char*
1150handleSubscriptionSource( 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
1173static const char*
1174handleRoamPref( 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}
1201static const char*
1202handleTech( 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
1239static const char*
1240handleEmergencyMode( 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
1273static const char*
1274handlePrlVersion( 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
1285static const char*
1286handleRadioPower( 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
1301static const char*
1302handleRadioPowerReq( 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
1310static const char*
1311handleSIMStatusReq( 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?
1329static const char*
1330handleSRegister( 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
1340static const char*
1341handleNetworkRegistration( 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
1415static const char*
1416handleSetDialTone( const char*  cmd, AModem  modem )
1417{
1418    /* XXX: TODO */
1419    return NULL;
1420}
1421
1422static const char*
1423handleDeleteSMSonSIM( const char*  cmd, AModem  modem )
1424{
1425    /* XXX: TODO */
1426    return NULL;
1427}
1428
1429static const char*
1430handleSIM_IO( const char*  cmd, AModem  modem )
1431{
1432    return asimcard_io( modem->sim, cmd );
1433}
1434
1435
1436static const char*
1437handleOperatorSelection( 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    }
1554BadCommand:
1555    return unknownCommand(cmd,modem);
1556}
1557
1558static const char*
1559handleRequestOperator( 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
1575static const char*
1576handleSendSMStoSIM( const char*  cmd, AModem  modem )
1577{
1578    /* XXX: TODO */
1579    return "ERROR: unimplemented";
1580}
1581
1582static const char*
1583handleSendSMS( const char*  cmd, AModem  modem )
1584{
1585    modem->wait_sms = 1;
1586    return "> ";
1587}
1588
1589#if 0
1590static void
1591sms_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
1611static void
1612smspdu_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
1648static const char*
1649handleSendSMSText( 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
1800static const char*
1801handleChangeOrEnterPIN( 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
1856static const char*
1857handleListCurrentCalls( 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 */
1884static void
1885amodem_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
1943static const char*
1944handleEndOfInit( const char*  cmd, AModem  modem )
1945{
1946    amodem_begin_line( modem );
1947    amodem_addTimeUpdate( modem );
1948    return amodem_end_line( modem );
1949}
1950
1951
1952static const char*
1953handleListPDPContexts( 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
1967static const char*
1968handleDefinePDPContext( 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;
2017BadCommand:
2018    return "ERROR: BAD COMMAND";
2019}
2020
2021static const char*
2022handleQueryPDPContext( 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
2042static const char*
2043handleStartPDPContext( const char*  cmd, AModem  modem )
2044{
2045    /* XXX: TODO: handle PDP start appropriately */
2046    return NULL;
2047}
2048
2049
2050static void
2051remote_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
2067static void
2068voice_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
2106static 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
2120static const char*
2121handleDial( 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
2176static const char*
2177handleAnswer( 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
2202int android_snapshot_update_time = 1;
2203int android_snapshot_update_time_request = 0;
2204
2205static const char*
2206handleSignalStrength( 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,85,130,90,6,4,25,9,50,68,12\r\n", rssi, ber );
2227    return amodem_end_line( modem );
2228}
2229
2230static const char*
2231handleHangup( 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 */
2340typedef const char*  (*ResponseHandler)(const char*  cmd, AModem  modem);
2341
2342static 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
2476const 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