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