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