android_modem.c revision e50d7246d699d293b3229cbaf29a328bf70b204c
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 84static const char* _amodem_switch_technology(AModem modem, AModemTech newtech, int32_t newpreferred); 85static int _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss); 86static int _amodem_set_cdma_prl_version( AModem modem, int prlVersion); 87 88#if DEBUG 89static 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 122extern AModemTech 123android_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 143extern ADataNetworkType 144android_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 */ 168typedef 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 */ 175typedef 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 */ 184typedef enum { 185 A_STATUS_UNKNOWN = 0, 186 A_STATUS_AVAILABLE, 187 A_STATUS_CURRENT, 188 A_STATUS_DENIED 189} AOperatorStatus; 190 191typedef struct { 192 AOperatorStatus status; 193 char name[3][16]; 194} AOperatorRec, *AOperator; 195 196typedef 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 205typedef enum { 206 A_DATA_IP = 0, 207 A_DATA_PPP 208} ADataType; 209 210#define A_DATA_APN_SIZE 32 211 212typedef 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 229typedef 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 304static void 305amodem_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 317void 318amodem_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 342static const char* 343amodem_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 353static void 354amodem_begin_line( AModem modem ) 355{ 356 modem->out_size = 0; 357} 358 359static void 360amodem_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 370static const char* 371amodem_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 392static AConfig * 393amodem_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 405static int 406amodem_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 424const char * 425amodem_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 442static 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 453static 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 464static void 465amodem_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 523static AVoiceCall amodem_alloc_call( AModem modem ); 524static void amodem_free_call( AModem modem, AVoiceCall call ); 525 526#define MODEM_DEV_STATE_SAVE_VERSION 1 527 528static 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 549static 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 576static AModemRec _android_modem[1]; 577 578AModem 579amodem_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 607void 608amodem_set_legacy( AModem modem ) 609{ 610 modem->supportsNetworkDataType = 0; 611} 612 613void 614amodem_destroy( AModem modem ) 615{ 616 asimcard_destroy( modem->sim ); 617 modem->sim = NULL; 618} 619 620 621static int 622amodem_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 631ARadioState 632amodem_get_radio_state( AModem modem ) 633{ 634 return modem->radio_state; 635} 636 637void 638amodem_set_radio_state( AModem modem, ARadioState state ) 639{ 640 modem->radio_state = state; 641} 642 643ASimCard 644amodem_get_sim( AModem modem ) 645{ 646 return modem->sim; 647} 648 649ARegistrationState 650amodem_get_voice_registration( AModem modem ) 651{ 652 return modem->voice_state; 653} 654 655void 656amodem_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, modem->cell_id ); 675 break; 676 default: 677 ; 678 } 679} 680 681ARegistrationState 682amodem_get_data_registration( AModem modem ) 683{ 684 return modem->data_state; 685} 686 687void 688amodem_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, modem->cell_id, 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, modem->cell_id ); 708 break; 709 710 default: 711 ; 712 } 713} 714 715static int 716amodem_nvram_set( AModem modem, const char *name, const char *value ) 717{ 718 aconfig_set(modem->nvram_config, name, value); 719 return 0; 720} 721static AModemTech 722tech_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 741void 742amodem_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 755int 756amodem_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 */ 779void 780amodem_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 **/ 802int 803amodem_get_call_count( AModem modem ) 804{ 805 return modem->call_count; 806} 807 808ACall 809amodem_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 817static AVoiceCall 818amodem_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 849static void 850amodem_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 878static AVoiceCall 879amodem_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 891static void 892amodem_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 900int 901amodem_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 928ACall 929amodem_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 944void 945amodem_set_signal_strength( AModem modem, int rssi, int ber ) 946{ 947 modem->rssi = rssi; 948 modem->ber = ber; 949} 950 951static void 952acall_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 978int 979amodem_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 992int 993amodem_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 1008static const char* 1009unknownCommand( 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 */ 1024static 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 1037static AModemTech 1038chooseTechFromMask( 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 1058static 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 1086static int 1087parsePreferred( 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 1104void 1105amodem_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 1113static 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 1124void 1125amodem_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 1134static 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 1149static const char* 1150handleSubscriptionSource( 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 1173static const char* 1174handleRoamPref( 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} 1201static const char* 1202handleTech( 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 1239static const char* 1240handleEmergencyMode( 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 1273static const char* 1274handlePrlVersion( 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 1285static const char* 1286handleRadioPower( 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 1301static const char* 1302handleRadioPowerReq( 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 1310static const char* 1311handleSIMStatusReq( 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? 1329static const char* 1330handleSRegister( 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 1340static const char* 1341handleNetworkRegistration( const char* cmd, AModem modem ) 1342{ 1343 if ( !memcmp( cmd, "+CREG", 5 ) ) { 1344 cmd += 5; 1345 if (cmd[0] == '?') { 1346 return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"", 1347 modem->voice_mode, modem->voice_state, 1348 modem->area_code, modem->cell_id ); 1349 } else if (cmd[0] == '=') { 1350 switch (cmd[1]) { 1351 case '0': 1352 modem->voice_mode = A_REGISTRATION_UNSOL_DISABLED; 1353 break; 1354 1355 case '1': 1356 modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED; 1357 break; 1358 1359 case '2': 1360 modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 1361 break; 1362 1363 case '?': 1364 return "+CREG: (0-2)"; 1365 1366 default: 1367 return "ERROR: BAD COMMAND"; 1368 } 1369 } else { 1370 assert( 0 && "unreachable" ); 1371 } 1372 } else if ( !memcmp( cmd, "+CGREG", 6 ) ) { 1373 cmd += 6; 1374 if (cmd[0] == '?') { 1375 if (modem->supportsNetworkDataType) 1376 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"", 1377 modem->data_mode, modem->data_state, 1378 modem->area_code, modem->cell_id, 1379 modem->data_network ); 1380 else 1381 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"", 1382 modem->data_mode, modem->data_state, 1383 modem->area_code, modem->cell_id ); 1384 } else if (cmd[0] == '=') { 1385 switch (cmd[1]) { 1386 case '0': 1387 modem->data_mode = A_REGISTRATION_UNSOL_DISABLED; 1388 break; 1389 1390 case '1': 1391 modem->data_mode = A_REGISTRATION_UNSOL_ENABLED; 1392 break; 1393 1394 case '2': 1395 modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 1396 break; 1397 1398 case '?': 1399 return "+CGREG: (0-2)"; 1400 1401 default: 1402 return "ERROR: BAD COMMAND"; 1403 } 1404 } else { 1405 assert( 0 && "unreachable" ); 1406 } 1407 } 1408 return NULL; 1409} 1410 1411static const char* 1412handleSetDialTone( const char* cmd, AModem modem ) 1413{ 1414 /* XXX: TODO */ 1415 return NULL; 1416} 1417 1418static const char* 1419handleDeleteSMSonSIM( const char* cmd, AModem modem ) 1420{ 1421 /* XXX: TODO */ 1422 return NULL; 1423} 1424 1425static const char* 1426handleSIM_IO( const char* cmd, AModem modem ) 1427{ 1428 return asimcard_io( modem->sim, cmd ); 1429} 1430 1431 1432static const char* 1433handleOperatorSelection( const char* cmd, AModem modem ) 1434{ 1435 assert( !memcmp( "+COPS", cmd, 5 ) ); 1436 cmd += 5; 1437 if (cmd[0] == '?') { /* ask for current operator */ 1438 AOperator oper = &modem->operators[ modem->oper_index ]; 1439 1440 if ( !amodem_has_network( modem ) ) 1441 { 1442 /* this error code means "no network" */ 1443 return amodem_printf( modem, "+CME ERROR: 30" ); 1444 } 1445 1446 oper = &modem->operators[ modem->oper_index ]; 1447 1448 if ( modem->oper_name_index == 2 ) 1449 return amodem_printf( modem, "+COPS: %d,2,%s", 1450 modem->oper_selection_mode, 1451 oper->name[2] ); 1452 1453 return amodem_printf( modem, "+COPS: %d,%d,\"%s\"", 1454 modem->oper_selection_mode, 1455 modem->oper_name_index, 1456 oper->name[ modem->oper_name_index ] ); 1457 } 1458 else if (cmd[0] == '=' && cmd[1] == '?') { /* ask for all available operators */ 1459 const char* comma = "+COPS: "; 1460 int nn; 1461 amodem_begin_line( modem ); 1462 for (nn = 0; nn < modem->oper_count; nn++) { 1463 AOperator oper = &modem->operators[nn]; 1464 amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma, 1465 oper->status, oper->name[0], oper->name[1], oper->name[2] ); 1466 comma = ", "; 1467 } 1468 return amodem_end_line( modem ); 1469 } 1470 else if (cmd[0] == '=') { 1471 switch (cmd[1]) { 1472 case '0': 1473 modem->oper_selection_mode = A_SELECTION_AUTOMATIC; 1474 return NULL; 1475 1476 case '1': 1477 { 1478 int format, nn, len, found = -1; 1479 1480 if (cmd[2] != ',') 1481 goto BadCommand; 1482 format = cmd[3] - '0'; 1483 if ( (unsigned)format > 2 ) 1484 goto BadCommand; 1485 if (cmd[4] != ',') 1486 goto BadCommand; 1487 cmd += 5; 1488 len = strlen(cmd); 1489 if (*cmd == '"') { 1490 cmd++; 1491 len -= 2; 1492 } 1493 if (len <= 0) 1494 goto BadCommand; 1495 1496 for (nn = 0; nn < modem->oper_count; nn++) { 1497 AOperator oper = modem->operators + nn; 1498 char* name = oper->name[ format ]; 1499 1500 if ( !memcpy( name, cmd, len ) && name[len] == 0 ) { 1501 found = nn; 1502 break; 1503 } 1504 } 1505 1506 if (found < 0) { 1507 /* Selection failed */ 1508 return "+CME ERROR: 529"; 1509 } else if (modem->operators[found].status == A_STATUS_DENIED) { 1510 /* network not allowed */ 1511 return "+CME ERROR: 32"; 1512 } 1513 modem->oper_index = found; 1514 1515 /* set the voice and data registration states to home or roaming 1516 * depending on the operator index 1517 */ 1518 if (found == OPERATOR_HOME_INDEX) { 1519 modem->voice_state = A_REGISTRATION_HOME; 1520 modem->data_state = A_REGISTRATION_HOME; 1521 } else if (found == OPERATOR_ROAMING_INDEX) { 1522 modem->voice_state = A_REGISTRATION_ROAMING; 1523 modem->data_state = A_REGISTRATION_ROAMING; 1524 } 1525 return NULL; 1526 } 1527 1528 case '2': 1529 modem->oper_selection_mode = A_SELECTION_DEREGISTRATION; 1530 return NULL; 1531 1532 case '3': 1533 { 1534 int format; 1535 1536 if (cmd[2] != ',') 1537 goto BadCommand; 1538 1539 format = cmd[3] - '0'; 1540 if ( (unsigned)format > 2 ) 1541 goto BadCommand; 1542 1543 modem->oper_name_index = format; 1544 return NULL; 1545 } 1546 default: 1547 ; 1548 } 1549 } 1550BadCommand: 1551 return unknownCommand(cmd,modem); 1552} 1553 1554static const char* 1555handleRequestOperator( const char* cmd, AModem modem ) 1556{ 1557 AOperator oper; 1558 cmd=cmd; 1559 1560 if ( !amodem_has_network(modem) ) 1561 return "+CME ERROR: 30"; 1562 1563 oper = modem->operators + modem->oper_index; 1564 modem->oper_name_index = 2; 1565 return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r" 1566 "+COPS: 0,1,\"%s\"\r" 1567 "+COPS: 0,2,\"%s\"", 1568 oper->name[0], oper->name[1], oper->name[2] ); 1569} 1570 1571static const char* 1572handleSendSMStoSIM( const char* cmd, AModem modem ) 1573{ 1574 /* XXX: TODO */ 1575 return "ERROR: unimplemented"; 1576} 1577 1578static const char* 1579handleSendSMS( const char* cmd, AModem modem ) 1580{ 1581 modem->wait_sms = 1; 1582 return "> "; 1583} 1584 1585#if 0 1586static void 1587sms_address_dump( SmsAddress address, FILE* out ) 1588{ 1589 int nn, len = address->len; 1590 1591 if (address->toa == 0x91) { 1592 fprintf( out, "+" ); 1593 } 1594 for (nn = 0; nn < len; nn += 2) 1595 { 1596 static const char dialdigits[16] = "0123456789*#,N%"; 1597 int c = address->data[nn/2]; 1598 1599 fprintf( out, "%c", dialdigits[c & 0xf] ); 1600 if (nn+1 >= len) 1601 break; 1602 1603 fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] ); 1604 } 1605} 1606 1607static void 1608smspdu_dump( SmsPDU pdu, FILE* out ) 1609{ 1610 SmsAddressRec address; 1611 unsigned char temp[256]; 1612 int len; 1613 1614 if (pdu == NULL) { 1615 fprintf( out, "SMS PDU is (null)\n" ); 1616 return; 1617 } 1618 1619 fprintf( out, "SMS PDU type: " ); 1620 switch (smspdu_get_type(pdu)) { 1621 case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break; 1622 case SMS_PDU_SUBMIT: fprintf(out, "SUBMIT"); break; 1623 case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break; 1624 default: fprintf(out, "UNKNOWN"); 1625 } 1626 fprintf( out, "\n sender: " ); 1627 if (smspdu_get_sender_address(pdu, &address) < 0) 1628 fprintf( out, "(N/A)" ); 1629 else 1630 sms_address_dump(&address, out); 1631 fprintf( out, "\n receiver: " ); 1632 if (smspdu_get_receiver_address(pdu, &address) < 0) 1633 fprintf(out, "(N/A)"); 1634 else 1635 sms_address_dump(&address, out); 1636 fprintf( out, "\n text: " ); 1637 len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 ); 1638 if (len > sizeof(temp)-1 ) 1639 len = sizeof(temp)-1; 1640 fprintf( out, "'%.*s'\n", len, temp ); 1641} 1642#endif 1643 1644static const char* 1645handleSendSMSText( const char* cmd, AModem modem ) 1646{ 1647#if 1 1648 SmsAddressRec address; 1649 char temp[16]; 1650 char number[16]; 1651 int numlen; 1652 int len = strlen(cmd); 1653 SmsPDU pdu; 1654 1655 /* get rid of trailing escape */ 1656 if (len > 0 && cmd[len-1] == 0x1a) 1657 len -= 1; 1658 1659 pdu = smspdu_create_from_hex( cmd, len ); 1660 if (pdu == NULL) { 1661 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd); 1662 return "+CMS ERROR: INVALID SMS PDU"; 1663 } 1664 if (smspdu_get_receiver_address(pdu, &address) < 0) { 1665 D("%s: could not get SMS receiver address from '%s'\n", 1666 __FUNCTION__, cmd); 1667 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS"; 1668 } 1669 1670 do { 1671 int index; 1672 1673 numlen = sms_address_to_str( &address, temp, sizeof(temp) ); 1674 if (numlen > sizeof(temp)-1) 1675 break; 1676 temp[numlen] = 0; 1677 1678 /* Converts 4, 7, and 10 digits number to 11 digits */ 1679 if (numlen == 10 && !strncmp(temp, PHONE_PREFIX+1, 6)) { 1680 memcpy( number, PHONE_PREFIX, 1 ); 1681 memcpy( number+1, temp, numlen ); 1682 number[numlen+1] = 0; 1683 } else if (numlen == 7 && !strncmp(temp, PHONE_PREFIX+4, 3)) { 1684 memcpy( number, PHONE_PREFIX, 4 ); 1685 memcpy( number+4, temp, numlen ); 1686 number[numlen+4] = 0; 1687 } else if (numlen == 4) { 1688 memcpy( number, PHONE_PREFIX, 7 ); 1689 memcpy( number+7, temp, numlen ); 1690 number[numlen+7] = 0; 1691 } else { 1692 memcpy( number, temp, numlen ); 1693 number[numlen] = 0; 1694 } 1695 1696 if ( remote_number_string_to_port( number ) < 0 ) 1697 break; 1698 1699 if (modem->sms_receiver == NULL) { 1700 modem->sms_receiver = sms_receiver_create(); 1701 if (modem->sms_receiver == NULL) { 1702 D( "%s: could not create SMS receiver\n", __FUNCTION__ ); 1703 break; 1704 } 1705 } 1706 1707 index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu ); 1708 if (index < 0) { 1709 D( "%s: could not add submit PDU\n", __FUNCTION__ ); 1710 break; 1711 } 1712 /* the PDU is now owned by the receiver */ 1713 pdu = NULL; 1714 1715 if (index > 0) { 1716 SmsAddressRec from[1]; 1717 char temp[12]; 1718 SmsPDU* deliver; 1719 int nn; 1720 1721 snprintf( temp, sizeof(temp), PHONE_PREFIX "%d", modem->base_port ); 1722 sms_address_from_str( from, temp, strlen(temp) ); 1723 1724 deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from ); 1725 if (deliver == NULL) { 1726 D( "%s: could not create deliver PDUs for SMS index %d\n", 1727 __FUNCTION__, index ); 1728 break; 1729 } 1730 1731 for (nn = 0; deliver[nn] != NULL; nn++) { 1732 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) { 1733 D( "%s: could not send SMS PDU to remote emulator\n", 1734 __FUNCTION__ ); 1735 break; 1736 } 1737 } 1738 1739 smspdu_free_list(deliver); 1740 } 1741 1742 } while (0); 1743 1744 if (pdu != NULL) 1745 smspdu_free(pdu); 1746 1747#elif 1 1748 SmsAddressRec address; 1749 char number[16]; 1750 int numlen; 1751 int len = strlen(cmd); 1752 SmsPDU pdu; 1753 1754 /* get rid of trailing escape */ 1755 if (len > 0 && cmd[len-1] == 0x1a) 1756 len -= 1; 1757 1758 pdu = smspdu_create_from_hex( cmd, len ); 1759 if (pdu == NULL) { 1760 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd); 1761 return "+CMS ERROR: INVALID SMS PDU"; 1762 } 1763 if (smspdu_get_receiver_address(pdu, &address) < 0) { 1764 D("%s: could not get SMS receiver address from '%s'\n", 1765 __FUNCTION__, cmd); 1766 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS"; 1767 } 1768 do { 1769 numlen = sms_address_to_str( &address, number, sizeof(number) ); 1770 if (numlen > sizeof(number)-1) 1771 break; 1772 1773 number[numlen] = 0; 1774 if ( remote_number_string_to_port( number ) < 0 ) 1775 break; 1776 1777 if ( remote_call_sms( number, modem->base_port, pdu ) < 0 ) 1778 { 1779 D("%s: could not send SMS PDU to remote emulator\n", 1780 __FUNCTION__); 1781 return "+CMS ERROR: NO EMULATOR RECEIVER"; 1782 } 1783 } while (0); 1784#else 1785 fprintf(stderr, "SMS<< %s\n", cmd); 1786 SmsPDU pdu = smspdu_create_from_hex( cmd, strlen(cmd) ); 1787 if (pdu == NULL) { 1788 fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd); 1789 } else { 1790 smspdu_dump(pdu, stderr); 1791 } 1792#endif 1793 return "+CMGS: 0\rOK\r"; 1794} 1795 1796static const char* 1797handleChangeOrEnterPIN( const char* cmd, AModem modem ) 1798{ 1799 assert( !memcmp( cmd, "+CPIN=", 6 ) ); 1800 cmd += 6; 1801 1802 switch (asimcard_get_status(modem->sim)) { 1803 case A_SIM_STATUS_ABSENT: 1804 return "+CME ERROR: SIM ABSENT"; 1805 1806 case A_SIM_STATUS_NOT_READY: 1807 return "+CME ERROR: SIM NOT READY"; 1808 1809 case A_SIM_STATUS_READY: 1810 /* this may be a request to change the PIN */ 1811 { 1812 if (strlen(cmd) == 9 && cmd[4] == ',') { 1813 char pin[5]; 1814 memcpy( pin, cmd, 4 ); pin[4] = 0; 1815 1816 if ( !asimcard_check_pin( modem->sim, pin ) ) 1817 return "+CME ERROR: BAD PIN"; 1818 1819 memcpy( pin, cmd+5, 4 ); 1820 asimcard_set_pin( modem->sim, pin ); 1821 return "+CPIN: READY"; 1822 } 1823 } 1824 break; 1825 1826 case A_SIM_STATUS_PIN: /* waiting for PIN */ 1827 if ( asimcard_check_pin( modem->sim, cmd ) ) 1828 return "+CPIN: READY"; 1829 else 1830 return "+CME ERROR: BAD PIN"; 1831 1832 case A_SIM_STATUS_PUK: 1833 if (strlen(cmd) == 9 && cmd[4] == ',') { 1834 char puk[5]; 1835 memcpy( puk, cmd, 4 ); 1836 puk[4] = 0; 1837 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) ) 1838 return "+CPIN: READY"; 1839 else 1840 return "+CME ERROR: BAD PUK"; 1841 } 1842 return "+CME ERROR: BAD PUK"; 1843 1844 default: 1845 return "+CPIN: PH-NET PIN"; 1846 } 1847 1848 return "+CME ERROR: BAD FORMAT"; 1849} 1850 1851 1852static const char* 1853handleListCurrentCalls( const char* cmd, AModem modem ) 1854{ 1855 int nn; 1856 amodem_begin_line( modem ); 1857 for (nn = 0; nn < modem->call_count; nn++) { 1858 AVoiceCall vcall = modem->calls + nn; 1859 ACall call = &vcall->call; 1860 if (call->mode == A_CALL_VOICE) 1861 amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n", 1862 call->id, call->dir, call->state, call->mode, 1863 call->multi, call->number, 129 ); 1864 } 1865 return amodem_end_line( modem ); 1866} 1867 1868/* Add a(n unsolicited) time response. 1869 * 1870 * retrieve the current time and zone in a format suitable 1871 * for %CTZV: unsolicited message 1872 * "yy/mm/dd,hh:mm:ss(+/-)tz" 1873 * mm is 0-based 1874 * tz is in number of quarter-hours 1875 * 1876 * it seems reference-ril doesn't parse the comma (,) as anything else than a token 1877 * separator, so use a column (:) instead, the Java parsing code won't see a difference 1878 * 1879 */ 1880static void 1881amodem_addTimeUpdate( AModem modem ) 1882{ 1883 time_t now = time(NULL); 1884 struct tm utc, local; 1885 long e_local, e_utc; 1886 long tzdiff; 1887 char tzname[64]; 1888 1889 tzset(); 1890 1891 utc = *gmtime( &now ); 1892 local = *localtime( &now ); 1893 1894 e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday); 1895 e_utc = utc.tm_min + 60*(utc.tm_hour + 24*utc.tm_yday); 1896 1897 if ( utc.tm_year < local.tm_year ) 1898 e_local += 24*60; 1899 else if ( utc.tm_year > local.tm_year ) 1900 e_utc += 24*60; 1901 1902 tzdiff = e_local - e_utc; /* timezone offset in minutes */ 1903 1904 /* retrieve a zoneinfo-compatible name for the host timezone 1905 */ 1906 { 1907 char* end = tzname + sizeof(tzname); 1908 char* p = bufprint_zoneinfo_timezone( tzname, end ); 1909 if (p >= end) 1910 strcpy(tzname, "Unknown/Unknown"); 1911 1912 /* now replace every / in the timezone name by a "!" 1913 * that's because the code that reads the CTZV line is 1914 * dumb and treats a / as a field separator... 1915 */ 1916 p = tzname; 1917 while (1) { 1918 p = strchr(p, '/'); 1919 if (p == NULL) 1920 break; 1921 *p = '!'; 1922 p += 1; 1923 } 1924 } 1925 1926 /* as a special extension, we append the name of the host's time zone to the 1927 * string returned with %CTZ. the system should contain special code to detect 1928 * and deal with this case (since it normally relied on the operator's country code 1929 * which is hard to simulate on a general-purpose computer 1930 */ 1931 amodem_add_line( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s\r\n", 1932 (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday, 1933 utc.tm_hour, utc.tm_min, utc.tm_sec, 1934 (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15, 1935 (local.tm_isdst > 0), 1936 tzname ); 1937} 1938 1939static const char* 1940handleEndOfInit( const char* cmd, AModem modem ) 1941{ 1942 amodem_begin_line( modem ); 1943 amodem_addTimeUpdate( modem ); 1944 return amodem_end_line( modem ); 1945} 1946 1947 1948static const char* 1949handleListPDPContexts( const char* cmd, AModem modem ) 1950{ 1951 int nn; 1952 assert( !memcmp( cmd, "+CGACT?", 7 ) ); 1953 amodem_begin_line( modem ); 1954 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) { 1955 ADataContext data = modem->data_contexts + nn; 1956 if (!data->active) 1957 continue; 1958 amodem_add_line( modem, "+CGACT: %d,%d\r", data->id, data->active ); 1959 } 1960 return amodem_end_line( modem ); 1961} 1962 1963static const char* 1964handleDefinePDPContext( const char* cmd, AModem modem ) 1965{ 1966 assert( !memcmp( cmd, "+CGDCONT=", 9 ) ); 1967 cmd += 9; 1968 if (cmd[0] == '?') { 1969 int nn; 1970 amodem_begin_line(modem); 1971 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) { 1972 ADataContext data = modem->data_contexts + nn; 1973 if (!data->active) 1974 continue; 1975 amodem_add_line( modem, "+CGDCONT: %d,%s,\"%s\",,0,0\r\n", 1976 data->id, 1977 data->type == A_DATA_IP ? "IP" : "PPP", 1978 data->apn ); 1979 } 1980 return amodem_end_line(modem); 1981 } else { 1982 /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */ 1983 int id = cmd[0] - '1'; 1984 ADataType type; 1985 char apn[32]; 1986 ADataContext data; 1987 1988 if ((unsigned)id > 3) 1989 goto BadCommand; 1990 1991 if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) { 1992 type = A_DATA_IP; 1993 cmd += 8; 1994 } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) { 1995 type = A_DATA_PPP; 1996 cmd += 9; 1997 } else 1998 goto BadCommand; 1999 2000 { 2001 const char* p = strchr( cmd, '"' ); 2002 int len; 2003 if (p == NULL) 2004 goto BadCommand; 2005 len = (int)( p - cmd ); 2006 if (len > sizeof(apn)-1 ) 2007 len = sizeof(apn)-1; 2008 memcpy( apn, cmd, len ); 2009 apn[len] = 0; 2010 } 2011 2012 data = modem->data_contexts + id; 2013 2014 data->id = id + 1; 2015 data->active = 1; 2016 data->type = type; 2017 memcpy( data->apn, apn, sizeof(data->apn) ); 2018 } 2019 return NULL; 2020BadCommand: 2021 return "ERROR: BAD COMMAND"; 2022} 2023 2024 2025static const char* 2026handleStartPDPContext( const char* cmd, AModem modem ) 2027{ 2028 /* XXX: TODO: handle PDP start appropriately */ 2029 /* for the moment, always return success */ 2030#if 0 2031 AVoiceCall vcall = amodem_alloc_call( modem ); 2032 ACall call = (ACall) vcall; 2033 if (call == NULL) { 2034 return "ERROR: TOO MANY CALLS"; 2035 } 2036 call->id = 1; 2037 call->dir = A_CALL_OUTBOUND; 2038 /* XXX: it would be better to delay this */ 2039 call->state = A_CALL_ACTIVE; 2040 call->mode = A_CALL_DATA; 2041 call->multi = 0; 2042 strcpy( call->number, "012345" ); 2043#endif 2044 return NULL; 2045} 2046 2047 2048static void 2049remote_voice_call_event( void* _vcall, int success ) 2050{ 2051 AVoiceCall vcall = _vcall; 2052 AModem modem = vcall->modem; 2053 2054 /* NOTE: success only means we could send the "gsm in new" command 2055 * to the remote emulator, nothing more */ 2056 2057 if (!success) { 2058 /* aargh, the remote emulator probably quitted at that point */ 2059 amodem_free_call(modem, vcall); 2060 amodem_send_calls_update(modem); 2061 } 2062} 2063 2064 2065static void 2066voice_call_event( void* _vcall ) 2067{ 2068 AVoiceCall vcall = _vcall; 2069 ACall call = &vcall->call; 2070 2071 switch (call->state) { 2072 case A_CALL_DIALING: 2073 call->state = A_CALL_ALERTING; 2074 2075 if (vcall->is_remote) { 2076 if ( remote_call_dial( call->number, 2077 vcall->modem->base_port, 2078 remote_voice_call_event, vcall ) < 0 ) 2079 { 2080 /* we could not connect, probably because the corresponding 2081 * emulator is not running, so simply destroy this call. 2082 * XXX: should we send some sort of message to indicate BAD NUMBER ? */ 2083 /* it seems the Android code simply waits for changes in the list */ 2084 amodem_free_call( vcall->modem, vcall ); 2085 } 2086 } else { 2087 /* this is not a remote emulator number, so just simulate 2088 * a small ringing delay */ 2089 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT, 2090 voice_call_event, vcall ); 2091 } 2092 break; 2093 2094 case A_CALL_ALERTING: 2095 call->state = A_CALL_ACTIVE; 2096 break; 2097 2098 default: 2099 assert( 0 && "unreachable event call state" ); 2100 } 2101 amodem_send_calls_update(vcall->modem); 2102} 2103 2104static int amodem_is_emergency( AModem modem, const char *number ) 2105{ 2106 int i; 2107 2108 if (!number) return 0; 2109 for (i = 0; i < MAX_EMERGENCY_NUMBERS; i++) { 2110 if ( modem->emergency_numbers[i] && !strcmp( number, modem->emergency_numbers[i] )) break; 2111 } 2112 2113 if (i < MAX_EMERGENCY_NUMBERS) return 1; 2114 2115 return 0; 2116} 2117 2118static const char* 2119handleDial( const char* cmd, AModem modem ) 2120{ 2121 AVoiceCall vcall = amodem_alloc_call( modem ); 2122 ACall call = &vcall->call; 2123 int len; 2124 2125 if (call == NULL) 2126 return "ERROR: TOO MANY CALLS"; 2127 2128 assert( cmd[0] == 'D' ); 2129 call->dir = A_CALL_OUTBOUND; 2130 call->state = A_CALL_DIALING; 2131 call->mode = A_CALL_VOICE; 2132 call->multi = 0; 2133 2134 cmd += 1; 2135 len = strlen(cmd); 2136 if (len > 0 && cmd[len-1] == ';') 2137 len--; 2138 if (len >= sizeof(call->number)) 2139 len = sizeof(call->number)-1; 2140 2141 /* Converts 4, 7, and 10 digits number to 11 digits */ 2142 if (len == 10 && !strncmp(cmd, PHONE_PREFIX+1, 6)) { 2143 memcpy( call->number, PHONE_PREFIX, 1 ); 2144 memcpy( call->number+1, cmd, len ); 2145 call->number[len+1] = 0; 2146 } else if (len == 7 && !strncmp(cmd, PHONE_PREFIX+4, 3)) { 2147 memcpy( call->number, PHONE_PREFIX, 4 ); 2148 memcpy( call->number+4, cmd, len ); 2149 call->number[len+4] = 0; 2150 } else if (len == 4) { 2151 memcpy( call->number, PHONE_PREFIX, 7 ); 2152 memcpy( call->number+7, cmd, len ); 2153 call->number[len+7] = 0; 2154 } else { 2155 memcpy( call->number, cmd, len ); 2156 call->number[len] = 0; 2157 } 2158 2159 amodem_begin_line( modem ); 2160 if (amodem_is_emergency(modem, call->number)) { 2161 modem->in_emergency_mode = 1; 2162 amodem_add_line( modem, "+WSOS: 1" ); 2163 } 2164 vcall->is_remote = (remote_number_string_to_port(call->number) > 0); 2165 2166 vcall->timer = sys_timer_create(); 2167 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL, 2168 voice_call_event, vcall ); 2169 2170 return amodem_end_line( modem ); 2171} 2172 2173 2174static const char* 2175handleAnswer( const char* cmd, AModem modem ) 2176{ 2177 int nn; 2178 for (nn = 0; nn < modem->call_count; nn++) { 2179 AVoiceCall vcall = modem->calls + nn; 2180 ACall call = &vcall->call; 2181 2182 if (cmd[0] == 'A') { 2183 if (call->state == A_CALL_INCOMING) { 2184 acall_set_state( vcall, A_CALL_ACTIVE ); 2185 } 2186 else if (call->state == A_CALL_ACTIVE) { 2187 acall_set_state( vcall, A_CALL_HELD ); 2188 } 2189 } else if (cmd[0] == 'H') { 2190 /* ATH: hangup, since user is busy */ 2191 if (call->state == A_CALL_INCOMING) { 2192 amodem_free_call( modem, vcall ); 2193 break; 2194 } 2195 } 2196 } 2197 return NULL; 2198} 2199 2200#if CONFIG_ANDROID_SNAPSHOTS 2201int android_snapshot_update_time = 1; 2202int android_snapshot_update_time_request = 0; 2203#endif 2204 2205static const char* 2206handleSignalStrength( const char* cmd, AModem modem ) 2207{ 2208 amodem_begin_line( modem ); 2209#if CONFIG_ANDROID_SNAPSHOTS 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#endif 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 2230static const char* 2231handleHangup( 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 */ 2340typedef const char* (*ResponseHandler)(const char* cmd, AModem modem); 2341 2342static 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?", "", 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 2413 { "+CGQREQ=1", NULL, NULL }, 2414 { "+CGQMIN=1", NULL, NULL }, 2415 { "+CGEREP=1,0", NULL, NULL }, 2416 { "+CGACT=1,0", NULL, NULL }, 2417 { "D*99***1#", NULL, handleStartPDPContext }, 2418 2419 /* see requestDial() */ 2420 { "!D", NULL, handleDial }, /* the code says that success/error is ignored, the call state will 2421 be polled through +CLCC instead */ 2422 2423 /* see requestSMSAcknowledge() */ 2424 { "+CNMA=1", NULL, NULL }, 2425 { "+CNMA=2", NULL, NULL }, 2426 2427 /* see requestSIM_IO() */ 2428 { "!+CRSM=", NULL, handleSIM_IO }, 2429 2430 /* see onRequest() */ 2431 { "+CHLD=0", NULL, handleHangup }, 2432 { "+CHLD=1", NULL, handleHangup }, 2433 { "+CHLD=2", NULL, handleHangup }, 2434 { "+CHLD=3", NULL, handleHangup }, 2435 { "A", NULL, handleAnswer }, /* answer the call */ 2436 { "H", NULL, handleAnswer }, /* user is busy */ 2437 { "!+VTS=", NULL, handleSetDialTone }, 2438 { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL }, /* request internation subscriber identification number */ 2439 { "+CGSN", "000000000000000", NULL }, /* request model version */ 2440 { "+CUSD=2",NULL, NULL }, /* Cancel USSD */ 2441 { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */ 2442 { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */ 2443 { "!+CPIN=", NULL, handleChangeOrEnterPIN }, 2444 2445 /* see getSIMStatus() */ 2446 { "+CPIN?", NULL, handleSIMStatusReq }, 2447 { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL }, 2448 2449 /* see isRadioOn() */ 2450 { "+CFUN?", NULL, handleRadioPowerReq }, 2451 2452 /* see initializeCallback() */ 2453 { "E0Q0V1", NULL, NULL }, 2454 { "S0=0", NULL, NULL }, 2455 { "+CMEE=1", NULL, NULL }, 2456 { "+CREG=2", NULL, handleNetworkRegistration }, 2457 { "+CREG=1", NULL, handleNetworkRegistration }, 2458 { "+CGREG=1", NULL, handleNetworkRegistration }, 2459 { "+CCWA=1", NULL, NULL }, 2460 { "+CMOD=0", NULL, NULL }, 2461 { "+CMUT=0", NULL, NULL }, 2462 { "+CSSN=0,1", NULL, NULL }, 2463 { "+COLP=0", NULL, NULL }, 2464 { "+CSCS=\"HEX\"", NULL, NULL }, 2465 { "+CUSD=1", NULL, NULL }, 2466 { "+CGEREP=1,0", NULL, NULL }, 2467 { "+CMGF=0", NULL, handleEndOfInit }, /* now is a goof time to send the current tme and timezone */ 2468 { "%CPI=3", NULL, NULL }, 2469 { "%CSTAT=1", NULL, NULL }, 2470 2471 /* end of list */ 2472 {NULL, NULL, NULL} 2473}; 2474 2475 2476#define REPLY(str) do { const char* s = (str); R(">> %s\n", quote(s)); return s; } while (0) 2477 2478const char* amodem_send( AModem modem, const char* cmd ) 2479{ 2480 const char* answer; 2481 2482 if ( modem->wait_sms != 0 ) { 2483 modem->wait_sms = 0; 2484 R( "SMS<< %s\n", quote(cmd) ); 2485 answer = handleSendSMSText( cmd, modem ); 2486 REPLY(answer); 2487 } 2488 2489 /* everything that doesn't start with 'AT' is not a command, right ? */ 2490 if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) { 2491 /* R( "-- %s\n", quote(cmd) ); */ 2492 return NULL; 2493 } 2494 R( "<< %s\n", quote(cmd) ); 2495 2496 cmd += 2; 2497 2498 /* TODO: implement command handling */ 2499 { 2500 int nn, found = 0; 2501 2502 for (nn = 0; ; nn++) { 2503 const char* scmd = sDefaultResponses[nn].cmd; 2504 2505 if (!scmd) /* end of list */ 2506 break; 2507 2508 if (scmd[0] == '!') { /* prefix match */ 2509 int len = strlen(++scmd); 2510 2511 if ( !memcmp( scmd, cmd, len ) ) { 2512 found = 1; 2513 break; 2514 } 2515 } else { /* full match */ 2516 if ( !strcmp( scmd, cmd ) ) { 2517 found = 1; 2518 break; 2519 } 2520 } 2521 } 2522 2523 if ( !found ) 2524 { 2525 D( "** UNSUPPORTED COMMAND **\n" ); 2526 REPLY( "ERROR: UNSUPPORTED" ); 2527 } 2528 else 2529 { 2530 const char* answer = sDefaultResponses[nn].answer; 2531 ResponseHandler handler = sDefaultResponses[nn].handler; 2532 2533 if ( answer != NULL ) { 2534 REPLY( amodem_printf( modem, "%s\rOK", answer ) ); 2535 } 2536 2537 if (handler == NULL) { 2538 REPLY( "OK" ); 2539 } 2540 2541 answer = handler( cmd, modem ); 2542 if (answer == NULL) 2543 REPLY( "OK" ); 2544 2545 if ( !memcmp( answer, "> ", 2 ) || 2546 !memcmp( answer, "ERROR", 5 ) || 2547 !memcmp( answer, "+CME ERROR", 6 ) ) 2548 { 2549 REPLY( answer ); 2550 } 2551 2552 if (answer != modem->out_buff) 2553 REPLY( amodem_printf( modem, "%s\rOK", answer ) ); 2554 2555 strcat( modem->out_buff, "\rOK" ); 2556 REPLY( answer ); 2557 } 2558 } 2559} 2560