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 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(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 612 amodem_set_legacy( AModem modem ) 613 { 614 modem->supportsNetworkDataType = 0; 615 } 616 617 void 618 amodem_destroy( AModem modem ) 619 { 620 asimcard_destroy( modem->sim ); 621 modem->sim = NULL; 622 } 623 624 625 static int 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 636 amodem_get_radio_state( AModem modem ) 637 { 638 return modem->radio_state; 639 } 640 641 void 642 amodem_set_radio_state( AModem modem, ARadioState state ) 643 { 644 modem->radio_state = state; 645 } 646 647 ASimCard 648 amodem_get_sim( AModem modem ) 649 { 650 return modem->sim; 651 } 652 653 ARegistrationState 654 amodem_get_voice_registration( AModem modem ) 655 { 656 return modem->voice_state; 657 } 658 659 void 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 686 amodem_get_data_registration( AModem modem ) 687 { 688 return modem->data_state; 689 } 690 691 void 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 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 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 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 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 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 807 amodem_get_call_count( AModem modem ) 808 { 809 return modem->call_count; 810 } 811 812 ACall 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 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 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 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 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 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 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 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 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 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 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* 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 */ 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 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* 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 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 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 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 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 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* 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* 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* 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* 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* 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* 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* 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* 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* 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* 1420 handleSetDialTone( const char* cmd, AModem modem ) 1421 { 1422 /* XXX: TODO */ 1423 return NULL; 1424 } 1425 1426 static const char* 1427 handleDeleteSMSonSIM( const char* cmd, AModem modem ) 1428 { 1429 /* XXX: TODO */ 1430 return NULL; 1431 } 1432 1433 static const char* 1434 handleSIM_IO( const char* cmd, AModem modem ) 1435 { 1436 return asimcard_io( modem->sim, cmd ); 1437 } 1438 1439 1440 static const char* 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* 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* 1580 handleSendSMStoSIM( const char* cmd, AModem modem ) 1581 { 1582 /* XXX: TODO */ 1583 return "ERROR: unimplemented"; 1584 } 1585 1586 static const char* 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* 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* 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* 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 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* 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* 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* 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* 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* 2047 handleStartPDPContext( const char* cmd, AModem modem ) 2048 { 2049 /* XXX: TODO: handle PDP start appropriately */ 2050 return NULL; 2051 } 2052 2053 2054 static void 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 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 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* 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* 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* 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* 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 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