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