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 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 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 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 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 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* 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 354 amodem_begin_line( AModem modem ) 355 { 356 modem->out_size = 0; 357 } 358 359 static void 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* 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 * 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 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 * 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 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 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 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 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 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 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 608 amodem_set_legacy( AModem modem ) 609 { 610 modem->supportsNetworkDataType = 0; 611 } 612 613 void 614 amodem_destroy( AModem modem ) 615 { 616 asimcard_destroy( modem->sim ); 617 modem->sim = NULL; 618 } 619 620 621 static int 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 632 amodem_get_radio_state( AModem modem ) 633 { 634 return modem->radio_state; 635 } 636 637 void 638 amodem_set_radio_state( AModem modem, ARadioState state ) 639 { 640 modem->radio_state = state; 641 } 642 643 ASimCard 644 amodem_get_sim( AModem modem ) 645 { 646 return modem->sim; 647 } 648 649 ARegistrationState 650 amodem_get_voice_registration( AModem modem ) 651 { 652 return modem->voice_state; 653 } 654 655 void 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 682 amodem_get_data_registration( AModem modem ) 683 { 684 return modem->data_state; 685 } 686 687 void 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 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 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 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 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 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 803 amodem_get_call_count( AModem modem ) 804 { 805 return modem->call_count; 806 } 807 808 ACall 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 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 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 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 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 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 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 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 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 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 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* 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 */ 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 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* 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 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 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 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 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 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* 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* 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* 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* 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* 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* 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* 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* 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* 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* 1416 handleSetDialTone( const char* cmd, AModem modem ) 1417 { 1418 /* XXX: TODO */ 1419 return NULL; 1420 } 1421 1422 static const char* 1423 handleDeleteSMSonSIM( const char* cmd, AModem modem ) 1424 { 1425 /* XXX: TODO */ 1426 return NULL; 1427 } 1428 1429 static const char* 1430 handleSIM_IO( const char* cmd, AModem modem ) 1431 { 1432 return asimcard_io( modem->sim, cmd ); 1433 } 1434 1435 1436 static const char* 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* 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* 1576 handleSendSMStoSIM( const char* cmd, AModem modem ) 1577 { 1578 /* XXX: TODO */ 1579 return "ERROR: unimplemented"; 1580 } 1581 1582 static const char* 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* 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* 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* 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 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* 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* 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* 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* 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* 2043 handleStartPDPContext( const char* cmd, AModem modem ) 2044 { 2045 /* XXX: TODO: handle PDP start appropriately */ 2046 return NULL; 2047 } 2048 2049 2050 static void 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 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 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* 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* 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* 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\r\n", rssi, ber ); 2227 return amodem_end_line( modem ); 2228 } 2229 2230 static const char* 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 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