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