android_modem.c revision a1b379c65f787fc85bd9c6f4a6d14d8a2bebc9d5
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/utils/debug.h" 15#include "android/utils/timezone.h" 16#include "android/utils/system.h" 17#include "sim_card.h" 18#include "sysdeps.h" 19#include <memory.h> 20#include <stdarg.h> 21#include <time.h> 22#include <assert.h> 23#include <stdio.h> 24#include "sms.h" 25#include "remote_call.h" 26 27#define DEBUG 1 28 29#if 1 30# define D_ACTIVE VERBOSE_CHECK(modem) 31#else 32# define D_ACTIVE DEBUG 33#endif 34 35#if 1 36# define R_ACTIVE VERBOSE_CHECK(radio) 37#else 38# define R_ACTIVE DEBUG 39#endif 40 41#if DEBUG 42# define D(...) do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0) 43# define R(...) do { if (R_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0) 44#else 45# define D(...) ((void)0) 46# define R(...) ((void)0) 47#endif 48 49#define CALL_DELAY_DIAL 1000 50#define CALL_DELAY_ALERT 1000 51 52/* the Android GSM stack checks that the operator's name has changed 53 * when roaming is on. If not, it will not update the Roaming status icon 54 * 55 * this means that we need to emulate two distinct operators: 56 * - the first one for the 'home' registration state, must also correspond 57 * to the emulated user's IMEI 58 * 59 * - the second one for the 'roaming' registration state, must have a 60 * different name and MCC/MNC 61 */ 62 63#define OPERATOR_HOME_INDEX 0 64#define OPERATOR_HOME_MCC 310 65#define OPERATOR_HOME_MNC 260 66#define OPERATOR_HOME_NAME "Android" 67#define OPERATOR_HOME_MCCMNC STRINGIFY(OPERATOR_HOME_MCC) \ 68 STRINGIFY(OPERATOR_HOME_MNC) 69 70#define OPERATOR_ROAMING_INDEX 1 71#define OPERATOR_ROAMING_MCC 310 72#define OPERATOR_ROAMING_MNC 295 73#define OPERATOR_ROAMING_NAME "TelKila" 74#define OPERATOR_ROAMING_MCCMNC STRINGIFY(OPERATOR_ROAMING_MCC) \ 75 STRINGIFY(OPERATOR_ROAMING_MNC) 76 77#if DEBUG 78static const char* quote( const char* line ) 79{ 80 static char temp[1024]; 81 const char* hexdigits = "0123456789abcdef"; 82 char* p = temp; 83 int c; 84 85 while ((c = *line++) != 0) { 86 c &= 255; 87 if (c >= 32 && c < 127) { 88 *p++ = c; 89 } 90 else if (c == '\r') { 91 memcpy( p, "<CR>", 4 ); 92 p += 4; 93 } 94 else if (c == '\n') { 95 memcpy( p, "<LF>", 4 );strcat( p, "<LF>" ); 96 p += 4; 97 } 98 else { 99 p[0] = '\\'; 100 p[1] = 'x'; 101 p[2] = hexdigits[ (c) >> 4 ]; 102 p[3] = hexdigits[ (c) & 15 ]; 103 p += 4; 104 } 105 } 106 *p = 0; 107 return temp; 108} 109#endif 110 111extern AGprsNetworkType 112android_parse_network_type( const char* speed ) 113{ 114 const struct { const char* name; AGprsNetworkType type; } types[] = { 115 { "gprs", A_GPRS_NETWORK_GPRS }, 116 { "edge", A_GPRS_NETWORK_EDGE }, 117 { "umts", A_GPRS_NETWORK_UMTS }, 118 { "hsdpa", A_GPRS_NETWORK_UMTS }, /* not handled yet by Android GSM framework */ 119 { "full", A_GPRS_NETWORK_UMTS }, 120 { NULL, 0 } 121 }; 122 int nn; 123 124 for (nn = 0; types[nn].name; nn++) { 125 if ( !strcmp(speed, types[nn].name) ) 126 return types[nn].type; 127 } 128 /* not found, be conservative */ 129 return A_GPRS_NETWORK_GPRS; 130} 131 132/* 'mode' for +CREG/+CGREG commands */ 133typedef enum { 134 A_REGISTRATION_UNSOL_DISABLED = 0, 135 A_REGISTRATION_UNSOL_ENABLED = 1, 136 A_REGISTRATION_UNSOL_ENABLED_FULL = 2 137} ARegistrationUnsolMode; 138 139/* Operator selection mode, see +COPS commands */ 140typedef enum { 141 A_SELECTION_AUTOMATIC, 142 A_SELECTION_MANUAL, 143 A_SELECTION_DEREGISTRATION, 144 A_SELECTION_SET_FORMAT, 145 A_SELECTION_MANUAL_AUTOMATIC 146} AOperatorSelection; 147 148/* Operator status, see +COPS commands */ 149typedef enum { 150 A_STATUS_UNKNOWN = 0, 151 A_STATUS_AVAILABLE, 152 A_STATUS_CURRENT, 153 A_STATUS_DENIED 154} AOperatorStatus; 155 156typedef struct { 157 AOperatorStatus status; 158 char name[3][16]; 159} AOperatorRec, *AOperator; 160 161typedef struct AVoiceCallRec { 162 ACallRec call; 163 SysTimer timer; 164 AModem modem; 165 char is_remote; 166} AVoiceCallRec, *AVoiceCall; 167 168#define MAX_OPERATORS 4 169 170typedef enum { 171 A_DATA_IP = 0, 172 A_DATA_PPP 173} ADataType; 174 175#define A_DATA_APN_SIZE 32 176 177typedef struct { 178 int id; 179 int active; 180 ADataType type; 181 char apn[ A_DATA_APN_SIZE ]; 182 183} ADataContextRec, *ADataContext; 184 185/* the spec says that there can only be a max of 4 contexts */ 186#define MAX_DATA_CONTEXTS 4 187#define MAX_CALLS 4 188 189#define A_MODEM_SELF_SIZE 3 190 191typedef struct AModemRec_ 192{ 193 /* Legacy support */ 194 char supportsNetworkDataType; 195 196 /* Radio state */ 197 ARadioState radio_state; 198 int area_code; 199 int cell_id; 200 int base_port; 201 202 /* SMS */ 203 int wait_sms; 204 205 /* SIM card */ 206 ASimCard sim; 207 208 /* voice and data network registration */ 209 ARegistrationUnsolMode voice_mode; 210 ARegistrationState voice_state; 211 ARegistrationUnsolMode data_mode; 212 ARegistrationState data_state; 213 AGprsNetworkType data_network; 214 215 /* operator names */ 216 AOperatorSelection oper_selection_mode; 217 ANameIndex oper_name_index; 218 int oper_index; 219 int oper_count; 220 AOperatorRec operators[ MAX_OPERATORS ]; 221 222 /* data connection contexts */ 223 ADataContextRec data_contexts[ MAX_DATA_CONTEXTS ]; 224 225 /* active calls */ 226 AVoiceCallRec calls[ MAX_CALLS ]; 227 int call_count; 228 229 /* unsolicited callback */ /* XXX: TODO: use this */ 230 AModemUnsolFunc unsol_func; 231 void* unsol_opaque; 232 233 SmsReceiver sms_receiver; 234 235 int out_size; 236 char out_buff[1024]; 237 238} AModemRec; 239 240 241static void 242amodem_unsol( AModem modem, const char* format, ... ) 243{ 244 if (modem->unsol_func) { 245 va_list args; 246 va_start(args, format); 247 vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args ); 248 va_end(args); 249 250 modem->unsol_func( modem->unsol_opaque, modem->out_buff ); 251 } 252} 253 254void 255amodem_receive_sms( AModem modem, SmsPDU sms ) 256{ 257#define SMS_UNSOL_HEADER "+CMT: 0\r\n" 258 259 if (modem->unsol_func) { 260 int len, max; 261 char* p; 262 263 strcpy( modem->out_buff, SMS_UNSOL_HEADER ); 264 p = modem->out_buff + (sizeof(SMS_UNSOL_HEADER)-1); 265 max = sizeof(modem->out_buff) - 3 - (sizeof(SMS_UNSOL_HEADER)-1); 266 len = smspdu_to_hex( sms, p, max ); 267 if (len > max) /* too long */ 268 return; 269 p[len] = '\r'; 270 p[len+1] = '\n'; 271 p[len+2] = 0; 272 273 R( "SMS>> %s\n", p ); 274 275 modem->unsol_func( modem->unsol_opaque, modem->out_buff ); 276 } 277} 278 279static const char* 280amodem_printf( AModem modem, const char* format, ... ) 281{ 282 va_list args; 283 va_start(args, format); 284 vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args ); 285 va_end(args); 286 287 return modem->out_buff; 288} 289 290static void 291amodem_begin_line( AModem modem ) 292{ 293 modem->out_size = 0; 294} 295 296static void 297amodem_add_line( AModem modem, const char* format, ... ) 298{ 299 va_list args; 300 va_start(args, format); 301 modem->out_size += vsnprintf( modem->out_buff + modem->out_size, 302 sizeof(modem->out_buff) - modem->out_size, 303 format, args ); 304 va_end(args); 305} 306 307static const char* 308amodem_end_line( AModem modem ) 309{ 310 modem->out_buff[ modem->out_size ] = 0; 311 return modem->out_buff; 312} 313 314static void 315amodem_reset( AModem modem ) 316{ 317 modem->radio_state = A_RADIO_STATE_OFF; 318 modem->wait_sms = 0; 319 320 modem->oper_name_index = 2; 321 modem->oper_selection_mode = A_SELECTION_AUTOMATIC; 322 modem->oper_index = 0; 323 modem->oper_count = 2; 324 325 modem->area_code = -1; 326 modem->cell_id = -1; 327 328 strcpy( modem->operators[0].name[0], OPERATOR_HOME_NAME ); 329 strcpy( modem->operators[0].name[1], OPERATOR_HOME_NAME ); 330 strcpy( modem->operators[0].name[2], OPERATOR_HOME_MCCMNC ); 331 332 modem->operators[0].status = A_STATUS_AVAILABLE; 333 334 strcpy( modem->operators[1].name[0], OPERATOR_ROAMING_NAME ); 335 strcpy( modem->operators[1].name[1], OPERATOR_ROAMING_NAME ); 336 strcpy( modem->operators[1].name[2], OPERATOR_ROAMING_MCCMNC ); 337 338 modem->operators[1].status = A_STATUS_AVAILABLE; 339 340 modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 341 modem->voice_state = A_REGISTRATION_HOME; 342 modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 343 modem->data_state = A_REGISTRATION_HOME; 344 modem->data_network = A_GPRS_NETWORK_UMTS; 345} 346 347static AModemRec _android_modem[1]; 348 349AModem 350amodem_create( int base_port, AModemUnsolFunc unsol_func, void* unsol_opaque ) 351{ 352 AModem modem = _android_modem; 353 354 amodem_reset( modem ); 355 modem->supportsNetworkDataType = 1; 356 modem->base_port = base_port; 357 modem->unsol_func = unsol_func; 358 modem->unsol_opaque = unsol_opaque; 359 360 modem->sim = asimcard_create(base_port); 361 362 return modem; 363} 364 365void 366amodem_set_legacy( AModem modem ) 367{ 368 modem->supportsNetworkDataType = 0; 369} 370 371void 372amodem_destroy( AModem modem ) 373{ 374 asimcard_destroy( modem->sim ); 375 modem->sim = NULL; 376} 377 378 379static int 380amodem_has_network( AModem modem ) 381{ 382 return !(modem->radio_state == A_RADIO_STATE_OFF || 383 modem->oper_index < 0 || 384 modem->oper_index >= modem->oper_count || 385 modem->oper_selection_mode == A_SELECTION_DEREGISTRATION ); 386} 387 388 389ARadioState 390amodem_get_radio_state( AModem modem ) 391{ 392 return modem->radio_state; 393} 394 395void 396amodem_set_radio_state( AModem modem, ARadioState state ) 397{ 398 modem->radio_state = state; 399} 400 401ASimCard 402amodem_get_sim( AModem modem ) 403{ 404 return modem->sim; 405} 406 407ARegistrationState 408amodem_get_voice_registration( AModem modem ) 409{ 410 return modem->voice_state; 411} 412 413void 414amodem_set_voice_registration( AModem modem, ARegistrationState state ) 415{ 416 modem->voice_state = state; 417 418 if (state == A_REGISTRATION_HOME) 419 modem->oper_index = OPERATOR_HOME_INDEX; 420 else if (state == A_REGISTRATION_ROAMING) 421 modem->oper_index = OPERATOR_ROAMING_INDEX; 422 423 switch (modem->voice_mode) { 424 case A_REGISTRATION_UNSOL_ENABLED: 425 amodem_unsol( modem, "+CREG: %d,%d\r", 426 modem->voice_mode, modem->voice_state ); 427 break; 428 429 case A_REGISTRATION_UNSOL_ENABLED_FULL: 430 amodem_unsol( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"\r", 431 modem->voice_mode, modem->voice_state, 432 modem->area_code, modem->cell_id ); 433 break; 434 default: 435 ; 436 } 437} 438 439ARegistrationState 440amodem_get_data_registration( AModem modem ) 441{ 442 return modem->data_state; 443} 444 445void 446amodem_set_data_registration( AModem modem, ARegistrationState state ) 447{ 448 modem->data_state = state; 449 450 switch (modem->data_mode) { 451 case A_REGISTRATION_UNSOL_ENABLED: 452 amodem_unsol( modem, "+CGREG: %d,%d\r", 453 modem->data_mode, modem->data_state ); 454 break; 455 456 case A_REGISTRATION_UNSOL_ENABLED_FULL: 457 if (modem->supportsNetworkDataType) 458 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"\r", 459 modem->data_mode, modem->data_state, 460 modem->area_code, modem->cell_id, 461 modem->data_network ); 462 else 463 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"\r", 464 modem->data_mode, modem->data_state, 465 modem->area_code, modem->cell_id ); 466 break; 467 468 default: 469 ; 470 } 471} 472 473void 474amodem_set_data_network_type( AModem modem, AGprsNetworkType type ) 475{ 476 modem->data_network = type; 477 amodem_set_data_registration( modem, modem->data_state ); 478} 479 480int 481amodem_get_operator_name ( AModem modem, ANameIndex index, char* buffer, int buffer_size ) 482{ 483 AOperator oper; 484 int len; 485 486 if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count || 487 (unsigned)index > 2 ) 488 return 0; 489 490 oper = modem->operators + modem->oper_index; 491 len = strlen(oper->name[index]) + 1; 492 493 if (buffer_size > len) 494 buffer_size = len; 495 496 if (buffer_size > 0) { 497 memcpy( buffer, oper->name[index], buffer_size-1 ); 498 buffer[buffer_size] = 0; 499 } 500 return len; 501} 502 503/* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */ 504void 505amodem_set_operator_name( AModem modem, ANameIndex index, const char* buffer, int buffer_size ) 506{ 507 AOperator oper; 508 int avail; 509 510 if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count || 511 (unsigned)index > 2 ) 512 return; 513 514 oper = modem->operators + modem->oper_index; 515 516 avail = sizeof(oper->name[0]); 517 if (buffer_size < 0) 518 buffer_size = strlen(buffer); 519 if (buffer_size > avail-1) 520 buffer_size = avail-1; 521 memcpy( oper->name[index], buffer, buffer_size ); 522 oper->name[index][buffer_size] = 0; 523} 524 525/** CALLS 526 **/ 527int 528amodem_get_call_count( AModem modem ) 529{ 530 return modem->call_count; 531} 532 533ACall 534amodem_get_call( AModem modem, int index ) 535{ 536 if ((unsigned)index >= (unsigned)modem->call_count) 537 return NULL; 538 539 return &modem->calls[index].call; 540} 541 542static AVoiceCall 543amodem_alloc_call( AModem modem ) 544{ 545 AVoiceCall call = NULL; 546 int count = modem->call_count; 547 548 if (count < MAX_CALLS) { 549 int id; 550 551 /* find a valid id for this call */ 552 for (id = 0; id < modem->call_count; id++) { 553 int found = 0; 554 int nn; 555 for (nn = 0; nn < count; nn++) { 556 if ( modem->calls[nn].call.id == (id+1) ) { 557 found = 1; 558 break; 559 } 560 } 561 if (!found) 562 break; 563 } 564 call = modem->calls + count; 565 call->call.id = id + 1; 566 call->modem = modem; 567 568 modem->call_count += 1; 569 } 570 return call; 571} 572 573 574static void 575amodem_free_call( AModem modem, AVoiceCall call ) 576{ 577 int nn; 578 579 if (call->timer) { 580 sys_timer_destroy( call->timer ); 581 call->timer = NULL; 582 } 583 584 if (call->is_remote) { 585 remote_call_cancel( call->call.number, modem->base_port ); 586 call->is_remote = 0; 587 } 588 589 for (nn = 0; nn < modem->call_count; nn++) { 590 if ( modem->calls + nn == call ) 591 break; 592 } 593 assert( nn < modem->call_count ); 594 595 memmove( modem->calls + nn, 596 modem->calls + nn + 1, 597 (modem->call_count - 1 - nn)*sizeof(*call) ); 598 599 modem->call_count -= 1; 600} 601 602 603static AVoiceCall 604amodem_find_call( AModem modem, int id ) 605{ 606 int nn; 607 608 for (nn = 0; nn < modem->call_count; nn++) { 609 AVoiceCall call = modem->calls + nn; 610 if (call->call.id == id) 611 return call; 612 } 613 return NULL; 614} 615 616static void 617amodem_send_calls_update( AModem modem ) 618{ 619 /* despite its name, this really tells the system that the call 620 * state has changed */ 621 amodem_unsol( modem, "RING\r" ); 622} 623 624 625int 626amodem_add_inbound_call( AModem modem, const char* number ) 627{ 628 AVoiceCall vcall = amodem_alloc_call( modem ); 629 ACall call = &vcall->call; 630 int len; 631 632 if (call == NULL) 633 return -1; 634 635 call->dir = A_CALL_INBOUND; 636 call->state = A_CALL_INCOMING; 637 call->mode = A_CALL_VOICE; 638 call->multi = 0; 639 640 vcall->is_remote = (remote_number_string_to_port(number) > 0); 641 642 len = strlen(number); 643 if (len >= sizeof(call->number)) 644 len = sizeof(call->number)-1; 645 646 memcpy( call->number, number, len ); 647 call->number[len] = 0; 648 649 amodem_send_calls_update( modem ); 650 return 0; 651} 652 653ACall 654amodem_find_call_by_number( AModem modem, const char* number ) 655{ 656 AVoiceCall vcall = modem->calls; 657 AVoiceCall vend = vcall + modem->call_count; 658 659 if (!number) 660 return NULL; 661 662 for ( ; vcall < vend; vcall++ ) 663 if ( !strcmp(vcall->call.number, number) ) 664 return &vcall->call; 665 666 return NULL; 667} 668 669 670static void 671acall_set_state( AVoiceCall call, ACallState state ) 672{ 673 if (state != call->call.state) 674 { 675 if (call->is_remote) 676 { 677 const char* number = call->call.number; 678 int port = call->modem->base_port; 679 680 switch (state) { 681 case A_CALL_HELD: 682 remote_call_other( number, port, REMOTE_CALL_HOLD ); 683 break; 684 685 case A_CALL_ACTIVE: 686 remote_call_other( number, port, REMOTE_CALL_ACCEPT ); 687 break; 688 689 default: ; 690 } 691 } 692 call->call.state = state; 693 } 694} 695 696 697int 698amodem_update_call( AModem modem, const char* fromNumber, ACallState state ) 699{ 700 AVoiceCall vcall = (AVoiceCall) amodem_find_call_by_number(modem, fromNumber); 701 702 if (vcall == NULL) 703 return -1; 704 705 acall_set_state( vcall, state ); 706 amodem_send_calls_update(modem); 707 return 0; 708} 709 710 711int 712amodem_disconnect_call( AModem modem, const char* number ) 713{ 714 AVoiceCall vcall = (AVoiceCall) amodem_find_call_by_number(modem, number); 715 716 if (!vcall) 717 return -1; 718 719 amodem_free_call( modem, vcall ); 720 amodem_send_calls_update(modem); 721 return 0; 722} 723 724/** COMMAND HANDLERS 725 **/ 726 727static const char* 728unknownCommand( const char* cmd, AModem modem ) 729{ 730 modem=modem; 731 fprintf(stderr, ">>> unknown command '%s'\n", cmd ); 732 return "ERROR: unknown command\r"; 733} 734 735static const char* 736handleRadioPower( const char* cmd, AModem modem ) 737{ 738 if ( !strcmp( cmd, "+CFUN=0" ) ) 739 { 740 /* turn radio off */ 741 modem->radio_state = A_RADIO_STATE_OFF; 742 } 743 else if ( !strcmp( cmd, "+CFUN=1" ) ) 744 { 745 /* turn radio on */ 746 modem->radio_state = A_RADIO_STATE_ON; 747 } 748 return NULL; 749} 750 751static const char* 752handleRadioPowerReq( const char* cmd, AModem modem ) 753{ 754 if (modem->radio_state != A_RADIO_STATE_OFF) 755 return "+CFUN=1"; 756 else 757 return "+CFUN=0"; 758} 759 760static const char* 761handleSIMStatusReq( const char* cmd, AModem modem ) 762{ 763 const char* answer = NULL; 764 765 switch (asimcard_get_status(modem->sim)) { 766 case A_SIM_STATUS_ABSENT: answer = "+CPIN: ABSENT"; break; 767 case A_SIM_STATUS_READY: answer = "+CPIN: READY"; break; 768 case A_SIM_STATUS_NOT_READY: answer = "+CMERROR: NOT READY"; break; 769 case A_SIM_STATUS_PIN: answer = "+CPIN: SIM PIN"; break; 770 case A_SIM_STATUS_PUK: answer = "+CPIN: SIM PUK"; break; 771 case A_SIM_STATUS_NETWORK_PERSONALIZATION: answer = "+CPIN: PH-NET PIN"; break; 772 default: 773 answer = "ERROR: internal error"; 774 } 775 return answer; 776} 777 778static const char* 779handleNetworkRegistration( const char* cmd, AModem modem ) 780{ 781 if ( !memcmp( cmd, "+CREG", 5 ) ) { 782 cmd += 5; 783 if (cmd[0] == '?') { 784 return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"", 785 modem->voice_mode, modem->voice_state, 786 modem->area_code, modem->cell_id ); 787 } else if (cmd[0] == '=') { 788 switch (cmd[1]) { 789 case '0': 790 modem->voice_mode = A_REGISTRATION_UNSOL_DISABLED; 791 break; 792 793 case '1': 794 modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED; 795 break; 796 797 case '2': 798 modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 799 break; 800 801 case '?': 802 return "+CREG: (0-2)"; 803 804 default: 805 return "ERROR: BAD COMMAND"; 806 } 807 } else { 808 assert( 0 && "unreachable" ); 809 } 810 } else if ( !memcmp( cmd, "+CGREG", 6 ) ) { 811 cmd += 6; 812 if (cmd[0] == '?') { 813 if (modem->supportsNetworkDataType) 814 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"", 815 modem->data_mode, modem->data_state, 816 modem->area_code, modem->cell_id, 817 modem->data_network ); 818 else 819 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"", 820 modem->data_mode, modem->data_state, 821 modem->area_code, modem->cell_id ); 822 } else if (cmd[0] == '=') { 823 switch (cmd[1]) { 824 case '0': 825 modem->data_mode = A_REGISTRATION_UNSOL_DISABLED; 826 break; 827 828 case '1': 829 modem->data_mode = A_REGISTRATION_UNSOL_ENABLED; 830 break; 831 832 case '2': 833 modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL; 834 break; 835 836 case '?': 837 return "+CGREG: (0-2)"; 838 839 default: 840 return "ERROR: BAD COMMAND"; 841 } 842 } else { 843 assert( 0 && "unreachable" ); 844 } 845 } 846 return NULL; 847} 848 849static const char* 850handleSetDialTone( const char* cmd, AModem modem ) 851{ 852 /* XXX: TODO */ 853 return NULL; 854} 855 856static const char* 857handleDeleteSMSonSIM( const char* cmd, AModem modem ) 858{ 859 /* XXX: TODO */ 860 return NULL; 861} 862 863static const char* 864handleSIM_IO( const char* cmd, AModem modem ) 865{ 866 return asimcard_io( modem->sim, cmd ); 867} 868 869 870static const char* 871handleOperatorSelection( const char* cmd, AModem modem ) 872{ 873 assert( !memcmp( "+COPS", cmd, 5 ) ); 874 cmd += 5; 875 if (cmd[0] == '?') { /* ask for current operator */ 876 AOperator oper = &modem->operators[ modem->oper_index ]; 877 878 if ( !amodem_has_network( modem ) ) 879 { 880 /* this error code means "no network" */ 881 return amodem_printf( modem, "+CME ERROR: 30" ); 882 } 883 884 oper = &modem->operators[ modem->oper_index ]; 885 886 if ( modem->oper_name_index == 2 ) 887 return amodem_printf( modem, "+COPS: %d,2,%s", 888 modem->oper_selection_mode, 889 oper->name[2] ); 890 891 return amodem_printf( modem, "+COPS: %d,%d,\"%s\"", 892 modem->oper_selection_mode, 893 modem->oper_name_index, 894 oper->name[ modem->oper_name_index ] ); 895 } 896 else if (cmd[0] == '=' && cmd[1] == '?') { /* ask for all available operators */ 897 const char* comma = "+COPS: "; 898 int nn; 899 amodem_begin_line( modem ); 900 for (nn = 0; nn < modem->oper_count; nn++) { 901 AOperator oper = &modem->operators[nn]; 902 amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma, 903 oper->status, oper->name[0], oper->name[1], oper->name[2] ); 904 comma = ", "; 905 } 906 return amodem_end_line( modem ); 907 } 908 else if (cmd[0] == '=') { 909 switch (cmd[1]) { 910 case '0': 911 modem->oper_selection_mode = A_SELECTION_AUTOMATIC; 912 return NULL; 913 914 case '1': 915 { 916 int format, nn, len, found = -1; 917 918 if (cmd[2] != ',') 919 goto BadCommand; 920 format = cmd[3] - '0'; 921 if ( (unsigned)format > 2 ) 922 goto BadCommand; 923 if (cmd[4] != ',') 924 goto BadCommand; 925 cmd += 5; 926 len = strlen(cmd); 927 if (*cmd == '"') { 928 cmd++; 929 len -= 2; 930 } 931 if (len <= 0) 932 goto BadCommand; 933 934 for (nn = 0; nn < modem->oper_count; nn++) { 935 AOperator oper = modem->operators + nn; 936 char* name = oper->name[ format ]; 937 938 if ( !memcpy( name, cmd, len ) && name[len] == 0 ) { 939 found = nn; 940 break; 941 } 942 } 943 944 if (found < 0) { 945 /* Selection failed */ 946 return "+CME ERROR: 529"; 947 } else if (modem->operators[found].status == A_STATUS_DENIED) { 948 /* network not allowed */ 949 return "+CME ERROR: 32"; 950 } 951 modem->oper_index = found; 952 953 /* set the voice and data registration states to home or roaming 954 * depending on the operator index 955 */ 956 if (found == OPERATOR_HOME_INDEX) { 957 modem->voice_state = A_REGISTRATION_HOME; 958 modem->data_state = A_REGISTRATION_HOME; 959 } else if (found == OPERATOR_ROAMING_INDEX) { 960 modem->voice_state = A_REGISTRATION_ROAMING; 961 modem->data_state = A_REGISTRATION_ROAMING; 962 } 963 return NULL; 964 } 965 966 case '2': 967 modem->oper_selection_mode = A_SELECTION_DEREGISTRATION; 968 return NULL; 969 970 case '3': 971 { 972 int format; 973 974 if (cmd[2] != ',') 975 goto BadCommand; 976 977 format = cmd[3] - '0'; 978 if ( (unsigned)format > 2 ) 979 goto BadCommand; 980 981 modem->oper_name_index = format; 982 return NULL; 983 } 984 default: 985 ; 986 } 987 } 988BadCommand: 989 return unknownCommand(cmd,modem); 990} 991 992static const char* 993handleRequestOperator( const char* cmd, AModem modem ) 994{ 995 AOperator oper; 996 cmd=cmd; 997 998 if ( !amodem_has_network(modem) ) 999 return "+CME ERROR: 30"; 1000 1001 oper = modem->operators + modem->oper_index; 1002 modem->oper_name_index = 2; 1003 return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r" 1004 "+COPS: 0,1,\"%s\"\r" 1005 "+COPS: 0,2,\"%s\"", 1006 oper->name[0], oper->name[1], oper->name[2] ); 1007} 1008 1009static const char* 1010handleSendSMStoSIM( const char* cmd, AModem modem ) 1011{ 1012 /* XXX: TODO */ 1013 return "ERROR: unimplemented"; 1014} 1015 1016static const char* 1017handleSendSMS( const char* cmd, AModem modem ) 1018{ 1019 modem->wait_sms = 1; 1020 return "> "; 1021} 1022 1023#if 0 1024static void 1025sms_address_dump( SmsAddress address, FILE* out ) 1026{ 1027 int nn, len = address->len; 1028 1029 if (address->toa == 0x91) { 1030 fprintf( out, "+" ); 1031 } 1032 for (nn = 0; nn < len; nn += 2) 1033 { 1034 static const char dialdigits[16] = "0123456789*#,N%"; 1035 int c = address->data[nn/2]; 1036 1037 fprintf( out, "%c", dialdigits[c & 0xf] ); 1038 if (nn+1 >= len) 1039 break; 1040 1041 fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] ); 1042 } 1043} 1044 1045static void 1046smspdu_dump( SmsPDU pdu, FILE* out ) 1047{ 1048 SmsAddressRec address; 1049 unsigned char temp[256]; 1050 int len; 1051 1052 if (pdu == NULL) { 1053 fprintf( out, "SMS PDU is (null)\n" ); 1054 return; 1055 } 1056 1057 fprintf( out, "SMS PDU type: " ); 1058 switch (smspdu_get_type(pdu)) { 1059 case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break; 1060 case SMS_PDU_SUBMIT: fprintf(out, "SUBMIT"); break; 1061 case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break; 1062 default: fprintf(out, "UNKNOWN"); 1063 } 1064 fprintf( out, "\n sender: " ); 1065 if (smspdu_get_sender_address(pdu, &address) < 0) 1066 fprintf( out, "(N/A)" ); 1067 else 1068 sms_address_dump(&address, out); 1069 fprintf( out, "\n receiver: " ); 1070 if (smspdu_get_receiver_address(pdu, &address) < 0) 1071 fprintf(out, "(N/A)"); 1072 else 1073 sms_address_dump(&address, out); 1074 fprintf( out, "\n text: " ); 1075 len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 ); 1076 if (len > sizeof(temp)-1 ) 1077 len = sizeof(temp)-1; 1078 fprintf( out, "'%.*s'\n", len, temp ); 1079} 1080#endif 1081 1082static const char* 1083handleSendSMSText( const char* cmd, AModem modem ) 1084{ 1085#if 1 1086 SmsAddressRec address; 1087 char temp[16]; 1088 char number[16]; 1089 int numlen; 1090 int len = strlen(cmd); 1091 SmsPDU pdu; 1092 1093 /* get rid of trailing escape */ 1094 if (len > 0 && cmd[len-1] == 0x1a) 1095 len -= 1; 1096 1097 pdu = smspdu_create_from_hex( cmd, len ); 1098 if (pdu == NULL) { 1099 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd); 1100 return "+CMS ERROR: INVALID SMS PDU"; 1101 } 1102 if (smspdu_get_receiver_address(pdu, &address) < 0) { 1103 D("%s: could not get SMS receiver address from '%s'\n", 1104 __FUNCTION__, cmd); 1105 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS"; 1106 } 1107 1108 do { 1109 int index; 1110 1111 numlen = sms_address_to_str( &address, temp, sizeof(temp) ); 1112 if (numlen > sizeof(temp)-1) 1113 break; 1114 temp[numlen] = 0; 1115 1116 /* Converts 4, 7, and 10 digits number to 11 digits */ 1117 if (numlen == 10 && !strncmp(temp, PHONE_PREFIX+1, 6)) { 1118 memcpy( number, PHONE_PREFIX, 1 ); 1119 memcpy( number+1, temp, numlen ); 1120 number[numlen+1] = 0; 1121 } else if (numlen == 7 && !strncmp(temp, PHONE_PREFIX+4, 3)) { 1122 memcpy( number, PHONE_PREFIX, 4 ); 1123 memcpy( number+4, temp, numlen ); 1124 number[numlen+4] = 0; 1125 } else if (numlen == 4) { 1126 memcpy( number, PHONE_PREFIX, 7 ); 1127 memcpy( number+7, temp, numlen ); 1128 number[numlen+7] = 0; 1129 } else { 1130 memcpy( number, temp, numlen ); 1131 number[numlen] = 0; 1132 } 1133 1134 if ( remote_number_string_to_port( number ) < 0 ) 1135 break; 1136 1137 if (modem->sms_receiver == NULL) { 1138 modem->sms_receiver = sms_receiver_create(); 1139 if (modem->sms_receiver == NULL) { 1140 D( "%s: could not create SMS receiver\n", __FUNCTION__ ); 1141 break; 1142 } 1143 } 1144 1145 index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu ); 1146 if (index < 0) { 1147 D( "%s: could not add submit PDU\n", __FUNCTION__ ); 1148 break; 1149 } 1150 /* the PDU is now owned by the receiver */ 1151 pdu = NULL; 1152 1153 if (index > 0) { 1154 SmsAddressRec from[1]; 1155 char temp[12]; 1156 SmsPDU* deliver; 1157 int nn; 1158 1159 snprintf( temp, sizeof(temp), PHONE_PREFIX "%d", modem->base_port ); 1160 sms_address_from_str( from, temp, strlen(temp) ); 1161 1162 deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from ); 1163 if (deliver == NULL) { 1164 D( "%s: could not create deliver PDUs for SMS index %d\n", 1165 __FUNCTION__, index ); 1166 break; 1167 } 1168 1169 for (nn = 0; deliver[nn] != NULL; nn++) { 1170 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) { 1171 D( "%s: could not send SMS PDU to remote emulator\n", 1172 __FUNCTION__ ); 1173 break; 1174 } 1175 } 1176 1177 smspdu_free_list(deliver); 1178 } 1179 1180 } while (0); 1181 1182 if (pdu != NULL) 1183 smspdu_free(pdu); 1184 1185#elif 1 1186 SmsAddressRec address; 1187 char number[16]; 1188 int numlen; 1189 int len = strlen(cmd); 1190 SmsPDU pdu; 1191 1192 /* get rid of trailing escape */ 1193 if (len > 0 && cmd[len-1] == 0x1a) 1194 len -= 1; 1195 1196 pdu = smspdu_create_from_hex( cmd, len ); 1197 if (pdu == NULL) { 1198 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd); 1199 return "+CMS ERROR: INVALID SMS PDU"; 1200 } 1201 if (smspdu_get_receiver_address(pdu, &address) < 0) { 1202 D("%s: could not get SMS receiver address from '%s'\n", 1203 __FUNCTION__, cmd); 1204 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS"; 1205 } 1206 do { 1207 numlen = sms_address_to_str( &address, number, sizeof(number) ); 1208 if (numlen > sizeof(number)-1) 1209 break; 1210 1211 number[numlen] = 0; 1212 if ( remote_number_string_to_port( number ) < 0 ) 1213 break; 1214 1215 if ( remote_call_sms( number, modem->base_port, pdu ) < 0 ) 1216 { 1217 D("%s: could not send SMS PDU to remote emulator\n", 1218 __FUNCTION__); 1219 return "+CMS ERROR: NO EMULATOR RECEIVER"; 1220 } 1221 } while (0); 1222#else 1223 fprintf(stderr, "SMS<< %s\n", cmd); 1224 SmsPDU pdu = smspdu_create_from_hex( cmd, strlen(cmd) ); 1225 if (pdu == NULL) { 1226 fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd); 1227 } else { 1228 smspdu_dump(pdu, stderr); 1229 } 1230#endif 1231 return "+CMGS: 0\rOK\r"; 1232} 1233 1234static const char* 1235handleChangeOrEnterPIN( const char* cmd, AModem modem ) 1236{ 1237 assert( !memcmp( cmd, "+CPIN=", 6 ) ); 1238 cmd += 6; 1239 1240 switch (asimcard_get_status(modem->sim)) { 1241 case A_SIM_STATUS_ABSENT: 1242 return "+CME ERROR: SIM ABSENT"; 1243 1244 case A_SIM_STATUS_NOT_READY: 1245 return "+CME ERROR: SIM NOT READY"; 1246 1247 case A_SIM_STATUS_READY: 1248 /* this may be a request to change the PIN */ 1249 { 1250 if (strlen(cmd) == 9 && cmd[4] == ',') { 1251 char pin[5]; 1252 memcpy( pin, cmd, 4 ); pin[4] = 0; 1253 1254 if ( !asimcard_check_pin( modem->sim, pin ) ) 1255 return "+CME ERROR: BAD PIN"; 1256 1257 memcpy( pin, cmd+5, 4 ); 1258 asimcard_set_pin( modem->sim, pin ); 1259 return "+CPIN: READY"; 1260 } 1261 } 1262 break; 1263 1264 case A_SIM_STATUS_PIN: /* waiting for PIN */ 1265 if ( asimcard_check_pin( modem->sim, cmd ) ) 1266 return "+CPIN: READY"; 1267 else 1268 return "+CME ERROR: BAD PIN"; 1269 1270 case A_SIM_STATUS_PUK: 1271 if (strlen(cmd) == 9 && cmd[4] == ',') { 1272 char puk[5]; 1273 memcpy( puk, cmd, 4 ); 1274 puk[4] = 0; 1275 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) ) 1276 return "+CPIN: READY"; 1277 else 1278 return "+CME ERROR: BAD PUK"; 1279 } 1280 return "+CME ERROR: BAD PUK"; 1281 1282 default: 1283 return "+CPIN: PH-NET PIN"; 1284 } 1285 1286 return "+CME ERROR: BAD FORMAT"; 1287} 1288 1289 1290static const char* 1291handleListCurrentCalls( const char* cmd, AModem modem ) 1292{ 1293 int nn; 1294 amodem_begin_line( modem ); 1295 for (nn = 0; nn < modem->call_count; nn++) { 1296 AVoiceCall vcall = modem->calls + nn; 1297 ACall call = &vcall->call; 1298 if (call->mode == A_CALL_VOICE) 1299 amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n", 1300 call->id, call->dir, call->state, call->mode, 1301 call->multi, call->number, 129 ); 1302 } 1303 return amodem_end_line( modem ); 1304} 1305 1306/* retrieve the current time and zone in a format suitable 1307 * for %CTZV: unsolicited message 1308 * "yy/mm/dd,hh:mm:ss(+/-)tz" 1309 * mm is 0-based 1310 * tz is in number of quarter-hours 1311 * 1312 * it seems reference-ril doesn't parse the comma (,) as anything else than a token 1313 * separator, so use a column (:) instead, the Java parsing code won't see a difference 1314 * 1315 */ 1316static const char* 1317handleEndOfInit( const char* cmd, AModem modem ) 1318{ 1319 time_t now = time(NULL); 1320 struct tm utc, local; 1321 long e_local, e_utc; 1322 long tzdiff; 1323 char tzname[64]; 1324 1325 tzset(); 1326 1327 utc = *gmtime( &now ); 1328 local = *localtime( &now ); 1329 1330 e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday); 1331 e_utc = utc.tm_min + 60*(utc.tm_hour + 24*utc.tm_yday); 1332 1333 if ( utc.tm_year < local.tm_year ) 1334 e_local += 24*60; 1335 else if ( utc.tm_year > local.tm_year ) 1336 e_utc += 24*60; 1337 1338 tzdiff = e_local - e_utc; /* timezone offset in minutes */ 1339 1340 /* retrieve a zoneinfo-compatible name for the host timezone 1341 */ 1342 { 1343 char* end = tzname + sizeof(tzname); 1344 char* p = bufprint_zoneinfo_timezone( tzname, end ); 1345 if (p >= end) 1346 strcpy(tzname, "Unknown/Unknown"); 1347 1348 /* now replace every / in the timezone name by a "!" 1349 * that's because the code that reads the CTZV line is 1350 * dumb and treats a / as a field separator... 1351 */ 1352 p = tzname; 1353 while (1) { 1354 p = strchr(p, '/'); 1355 if (p == NULL) 1356 break; 1357 *p = '!'; 1358 p += 1; 1359 } 1360 } 1361 1362 /* as a special extension, we append the name of the host's time zone to the 1363 * string returned with %CTZ. the system should contain special code to detect 1364 * and deal with this case (since it normally relied on the operator's country code 1365 * which is hard to simulate on a general-purpose computer 1366 */ 1367 return amodem_printf( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s", 1368 (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec, 1369 (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15, 1370 (local.tm_isdst > 0), 1371 tzname ); 1372} 1373 1374 1375static const char* 1376handleListPDPContexts( const char* cmd, AModem modem ) 1377{ 1378 int nn; 1379 assert( !memcmp( cmd, "+CGACT?", 7 ) ); 1380 amodem_begin_line( modem ); 1381 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) { 1382 ADataContext data = modem->data_contexts + nn; 1383 if (!data->active) 1384 continue; 1385 amodem_add_line( modem, "+CGACT: %d,%d\r", data->id, data->active ); 1386 } 1387 return amodem_end_line( modem ); 1388} 1389 1390static const char* 1391handleDefinePDPContext( const char* cmd, AModem modem ) 1392{ 1393 assert( !memcmp( cmd, "+CGDCONT=", 9 ) ); 1394 cmd += 9; 1395 if (cmd[0] == '?') { 1396 int nn; 1397 amodem_begin_line(modem); 1398 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) { 1399 ADataContext data = modem->data_contexts + nn; 1400 if (!data->active) 1401 continue; 1402 amodem_add_line( modem, "+CGDCONT: %d,%s,\"%s\",,0,0\r\n", 1403 data->id, 1404 data->type == A_DATA_IP ? "IP" : "PPP", 1405 data->apn ); 1406 } 1407 return amodem_end_line(modem); 1408 } else { 1409 /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */ 1410 int id = cmd[0] - '1'; 1411 ADataType type; 1412 char apn[32]; 1413 ADataContext data; 1414 1415 if ((unsigned)id > 3) 1416 goto BadCommand; 1417 1418 if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) { 1419 type = A_DATA_IP; 1420 cmd += 8; 1421 } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) { 1422 type = A_DATA_PPP; 1423 cmd += 9; 1424 } else 1425 goto BadCommand; 1426 1427 { 1428 const char* p = strchr( cmd, '"' ); 1429 int len; 1430 if (p == NULL) 1431 goto BadCommand; 1432 len = (int)( p - cmd ); 1433 if (len > sizeof(apn)-1 ) 1434 len = sizeof(apn)-1; 1435 memcpy( apn, cmd, len ); 1436 apn[len] = 0; 1437 } 1438 1439 data = modem->data_contexts + id; 1440 1441 data->id = id + 1; 1442 data->active = 1; 1443 data->type = type; 1444 memcpy( data->apn, apn, sizeof(data->apn) ); 1445 } 1446 return NULL; 1447BadCommand: 1448 return "ERROR: BAD COMMAND"; 1449} 1450 1451 1452static const char* 1453handleStartPDPContext( const char* cmd, AModem modem ) 1454{ 1455 /* XXX: TODO: handle PDP start appropriately */ 1456 /* for the moment, always return success */ 1457#if 0 1458 AVoiceCall vcall = amodem_alloc_call( modem ); 1459 ACall call = (ACall) vcall; 1460 if (call == NULL) { 1461 return "ERROR: TOO MANY CALLS"; 1462 } 1463 call->id = 1; 1464 call->dir = A_CALL_OUTBOUND; 1465 /* XXX: it would be better to delay this */ 1466 call->state = A_CALL_ACTIVE; 1467 call->mode = A_CALL_DATA; 1468 call->multi = 0; 1469 strcpy( call->number, "012345" ); 1470#endif 1471 return NULL; 1472} 1473 1474 1475static void 1476remote_voice_call_event( void* _vcall, int success ) 1477{ 1478 AVoiceCall vcall = _vcall; 1479 AModem modem = vcall->modem; 1480 1481 /* NOTE: success only means we could send the "gsm in new" command 1482 * to the remote emulator, nothing more */ 1483 1484 if (!success) { 1485 /* aargh, the remote emulator probably quitted at that point */ 1486 amodem_free_call(modem, vcall); 1487 amodem_send_calls_update(modem); 1488 } 1489} 1490 1491 1492static void 1493voice_call_event( void* _vcall ) 1494{ 1495 AVoiceCall vcall = _vcall; 1496 ACall call = &vcall->call; 1497 1498 switch (call->state) { 1499 case A_CALL_DIALING: 1500 call->state = A_CALL_ALERTING; 1501 1502 if (vcall->is_remote) { 1503 if ( remote_call_dial( call->number, 1504 vcall->modem->base_port, 1505 remote_voice_call_event, vcall ) < 0 ) 1506 { 1507 /* we could not connect, probably because the corresponding 1508 * emulator is not running, so simply destroy this call. 1509 * XXX: should we send some sort of message to indicate BAD NUMBER ? */ 1510 /* it seems the Android code simply waits for changes in the list */ 1511 amodem_free_call( vcall->modem, vcall ); 1512 } 1513 } else { 1514 /* this is not a remote emulator number, so just simulate 1515 * a small ringing delay */ 1516 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT, 1517 voice_call_event, vcall ); 1518 } 1519 break; 1520 1521 case A_CALL_ALERTING: 1522 call->state = A_CALL_ACTIVE; 1523 break; 1524 1525 default: 1526 assert( 0 && "unreachable event call state" ); 1527 } 1528 amodem_send_calls_update(vcall->modem); 1529} 1530 1531 1532static const char* 1533handleDial( const char* cmd, AModem modem ) 1534{ 1535 AVoiceCall vcall = amodem_alloc_call( modem ); 1536 ACall call = &vcall->call; 1537 int len; 1538 1539 if (call == NULL) 1540 return "ERROR: TOO MANY CALLS"; 1541 1542 assert( cmd[0] == 'D' ); 1543 call->dir = A_CALL_OUTBOUND; 1544 call->state = A_CALL_DIALING; 1545 call->mode = A_CALL_VOICE; 1546 call->multi = 0; 1547 1548 cmd += 1; 1549 len = strlen(cmd); 1550 if (len > 0 && cmd[len-1] == ';') 1551 len--; 1552 if (len >= sizeof(call->number)) 1553 len = sizeof(call->number)-1; 1554 1555 /* Converts 4, 7, and 10 digits number to 11 digits */ 1556 if (len == 10 && !strncmp(cmd, PHONE_PREFIX+1, 6)) { 1557 memcpy( call->number, PHONE_PREFIX, 1 ); 1558 memcpy( call->number+1, cmd, len ); 1559 call->number[len+1] = 0; 1560 } else if (len == 7 && !strncmp(cmd, PHONE_PREFIX+4, 3)) { 1561 memcpy( call->number, PHONE_PREFIX, 4 ); 1562 memcpy( call->number+4, cmd, len ); 1563 call->number[len+4] = 0; 1564 } else if (len == 4) { 1565 memcpy( call->number, PHONE_PREFIX, 7 ); 1566 memcpy( call->number+7, cmd, len ); 1567 call->number[len+7] = 0; 1568 } else { 1569 memcpy( call->number, cmd, len ); 1570 call->number[len] = 0; 1571 } 1572 1573 vcall->is_remote = (remote_number_string_to_port(call->number) > 0); 1574 1575 vcall->timer = sys_timer_create(); 1576 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL, 1577 voice_call_event, vcall ); 1578 1579 return NULL; 1580} 1581 1582 1583static const char* 1584handleAnswer( const char* cmd, AModem modem ) 1585{ 1586 int nn; 1587 for (nn = 0; nn < modem->call_count; nn++) { 1588 AVoiceCall vcall = modem->calls + nn; 1589 ACall call = &vcall->call; 1590 1591 if (cmd[0] == 'A') { 1592 if (call->state == A_CALL_INCOMING) { 1593 acall_set_state( vcall, A_CALL_ACTIVE ); 1594 } 1595 else if (call->state == A_CALL_ACTIVE) { 1596 acall_set_state( vcall, A_CALL_HELD ); 1597 } 1598 } else if (cmd[0] == 'H') { 1599 /* ATH: hangup, since user is busy */ 1600 if (call->state == A_CALL_INCOMING) { 1601 amodem_free_call( modem, vcall ); 1602 break; 1603 } 1604 } 1605 } 1606 return NULL; 1607} 1608 1609static const char* 1610handleHangup( const char* cmd, AModem modem ) 1611{ 1612 if ( !memcmp(cmd, "+CHLD=", 6) ) { 1613 int nn; 1614 cmd += 6; 1615 switch (cmd[0]) { 1616 case '0': /* release all held, and set busy for waiting calls */ 1617 for (nn = 0; nn < modem->call_count; nn++) { 1618 AVoiceCall vcall = modem->calls + nn; 1619 ACall call = &vcall->call; 1620 if (call->mode != A_CALL_VOICE) 1621 continue; 1622 if (call->state == A_CALL_HELD || 1623 call->state == A_CALL_WAITING || 1624 call->state == A_CALL_INCOMING) { 1625 amodem_free_call(modem, vcall); 1626 nn--; 1627 } 1628 } 1629 break; 1630 1631 case '1': 1632 if (cmd[1] == 0) { /* release all active, accept held one */ 1633 for (nn = 0; nn < modem->call_count; nn++) { 1634 AVoiceCall vcall = modem->calls + nn; 1635 ACall call = &vcall->call; 1636 if (call->mode != A_CALL_VOICE) 1637 continue; 1638 if (call->state == A_CALL_ACTIVE) { 1639 amodem_free_call(modem, vcall); 1640 nn--; 1641 } 1642 else if (call->state == A_CALL_HELD || 1643 call->state == A_CALL_WAITING) { 1644 acall_set_state( vcall, A_CALL_ACTIVE ); 1645 } 1646 } 1647 } else { /* release specific call */ 1648 int id = cmd[1] - '0'; 1649 AVoiceCall vcall = amodem_find_call( modem, id ); 1650 if (vcall != NULL) 1651 amodem_free_call( modem, vcall ); 1652 } 1653 break; 1654 1655 case '2': 1656 if (cmd[1] == 0) { /* place all active on hold, accept held or waiting one */ 1657 for (nn = 0; nn < modem->call_count; nn++) { 1658 AVoiceCall vcall = modem->calls + nn; 1659 ACall call = &vcall->call; 1660 if (call->mode != A_CALL_VOICE) 1661 continue; 1662 if (call->state == A_CALL_ACTIVE) { 1663 acall_set_state( vcall, A_CALL_HELD ); 1664 } 1665 else if (call->state == A_CALL_HELD || 1666 call->state == A_CALL_WAITING) { 1667 acall_set_state( vcall, A_CALL_ACTIVE ); 1668 } 1669 } 1670 } else { /* place all active on hold, except a specific one */ 1671 int id = cmd[1] - '0'; 1672 for (nn = 0; nn < modem->call_count; nn++) { 1673 AVoiceCall vcall = modem->calls + nn; 1674 ACall call = &vcall->call; 1675 if (call->mode != A_CALL_VOICE) 1676 continue; 1677 if (call->state == A_CALL_ACTIVE && call->id != id) { 1678 acall_set_state( vcall, A_CALL_HELD ); 1679 } 1680 } 1681 } 1682 break; 1683 1684 case '3': /* add a held call to the conversation */ 1685 for (nn = 0; nn < modem->call_count; nn++) { 1686 AVoiceCall vcall = modem->calls + nn; 1687 ACall call = &vcall->call; 1688 if (call->mode != A_CALL_VOICE) 1689 continue; 1690 if (call->state == A_CALL_HELD) { 1691 acall_set_state( vcall, A_CALL_ACTIVE ); 1692 break; 1693 } 1694 } 1695 break; 1696 1697 case '4': /* connect the two calls */ 1698 for (nn = 0; nn < modem->call_count; nn++) { 1699 AVoiceCall vcall = modem->calls + nn; 1700 ACall call = &vcall->call; 1701 if (call->mode != A_CALL_VOICE) 1702 continue; 1703 if (call->state == A_CALL_HELD) { 1704 acall_set_state( vcall, A_CALL_ACTIVE ); 1705 break; 1706 } 1707 } 1708 break; 1709 } 1710 } 1711 else 1712 return "ERROR: BAD COMMAND"; 1713 1714 return NULL; 1715} 1716 1717 1718/* a function used to deal with a non-trivial request */ 1719typedef const char* (*ResponseHandler)(const char* cmd, AModem modem); 1720 1721static const struct { 1722 const char* cmd; /* command coming from libreference-ril.so, if first 1723 character is '!', then the rest is a prefix only */ 1724 1725 const char* answer; /* default answer, NULL if needs specific handling or 1726 if OK is good enough */ 1727 1728 ResponseHandler handler; /* specific handler, ignored if 'answer' is not NULL, 1729 NULL if OK is good enough */ 1730} sDefaultResponses[] = 1731{ 1732 /* see onRadioPowerOn() */ 1733 { "%CPHS=1", NULL, NULL }, 1734 { "%CTZV=1", NULL, NULL }, 1735 1736 /* see onSIMReady() */ 1737 { "+CSMS=1", "+CSMS: 1, 1, 1", NULL }, 1738 { "+CNMI=1,2,2,1,1", NULL, NULL }, 1739 1740 /* see requestRadioPower() */ 1741 { "+CFUN=0", NULL, handleRadioPower }, 1742 { "+CFUN=1", NULL, handleRadioPower }, 1743 1744 /* see requestOrSendPDPContextList() */ 1745 { "+CGACT?", "", handleListPDPContexts }, 1746 1747 /* see requestOperator() */ 1748 { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator }, 1749 1750 /* see requestQueryNetworkSelectionMode() */ 1751 { "!+COPS", NULL, handleOperatorSelection }, 1752 1753 /* see requestGetCurrentCalls() */ 1754 { "+CLCC", NULL, handleListCurrentCalls }, 1755 1756 /* see requestWriteSmsToSim() */ 1757 { "!+CMGW=", NULL, handleSendSMStoSIM }, 1758 1759 /* see requestHangup() */ 1760 { "!+CHLD=", NULL, handleHangup }, 1761 1762 /* see requestSignalStrength() */ 1763 { "+CSQ", "+CSQ: 7,99", NULL }, /* XXX: TODO: implement variable signal strength and error rates */ 1764 1765 /* see requestRegistrationState() */ 1766 { "!+CREG", NULL, handleNetworkRegistration }, 1767 { "!+CGREG", NULL, handleNetworkRegistration }, 1768 1769 /* see requestSendSMS() */ 1770 { "!+CMGS=", NULL, handleSendSMS }, 1771 1772 /* see requestSetupDefaultPDP() */ 1773 { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL }, 1774 { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL }, 1775 1776 { "!+CGDCONT=", NULL, handleDefinePDPContext }, 1777 1778 { "+CGQREQ=1", NULL, NULL }, 1779 { "+CGQMIN=1", NULL, NULL }, 1780 { "+CGEREP=1,0", NULL, NULL }, 1781 { "+CGACT=1,0", NULL, NULL }, 1782 { "D*99***1#", NULL, handleStartPDPContext }, 1783 1784 /* see requestDial() */ 1785 { "!D", NULL, handleDial }, /* the code says that success/error is ignored, the call state will 1786 be polled through +CLCC instead */ 1787 1788 /* see requestSMSAcknowledge() */ 1789 { "+CNMA=1", NULL, NULL }, 1790 { "+CNMA=2", NULL, NULL }, 1791 1792 /* see requestSIM_IO() */ 1793 { "!+CRSM=", NULL, handleSIM_IO }, 1794 1795 /* see onRequest() */ 1796 { "+CHLD=0", NULL, handleHangup }, 1797 { "+CHLD=1", NULL, handleHangup }, 1798 { "+CHLD=2", NULL, handleHangup }, 1799 { "+CHLD=3", NULL, handleHangup }, 1800 { "A", NULL, handleAnswer }, /* answer the call */ 1801 { "H", NULL, handleAnswer }, /* user is busy */ 1802 { "!+VTS=", NULL, handleSetDialTone }, 1803 { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL }, /* request internation subscriber identification number */ 1804 { "+CGSN", "000000000000000", NULL }, /* request model version */ 1805 { "+CUSD=2",NULL, NULL }, /* Cancel USSD */ 1806 { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */ 1807 { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */ 1808 { "!+CPIN=", NULL, handleChangeOrEnterPIN }, 1809 1810 /* see getSIMStatus() */ 1811 { "+CPIN?", NULL, handleSIMStatusReq }, 1812 { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL }, 1813 1814 /* see isRadioOn() */ 1815 { "+CFUN?", NULL, handleRadioPowerReq }, 1816 1817 /* see initializeCallback() */ 1818 { "E0Q0V1", NULL, NULL }, 1819 { "S0=0", NULL, NULL }, 1820 { "+CMEE=1", NULL, NULL }, 1821 { "+CREG=2", NULL, handleNetworkRegistration }, 1822 { "+CREG=1", NULL, handleNetworkRegistration }, 1823 { "+CGREG=1", NULL, handleNetworkRegistration }, 1824 { "+CCWA=1", NULL, NULL }, 1825 { "+CMOD=0", NULL, NULL }, 1826 { "+CMUT=0", NULL, NULL }, 1827 { "+CSSN=0,1", NULL, NULL }, 1828 { "+COLP=0", NULL, NULL }, 1829 { "+CSCS=\"HEX\"", NULL, NULL }, 1830 { "+CUSD=1", NULL, NULL }, 1831 { "+CGEREP=1,0", NULL, NULL }, 1832 { "+CMGF=0", NULL, handleEndOfInit }, /* now is a goof time to send the current tme and timezone */ 1833 { "%CPI=3", NULL, NULL }, 1834 { "%CSTAT=1", NULL, NULL }, 1835 1836 /* end of list */ 1837 {NULL, NULL, NULL} 1838}; 1839 1840 1841#define REPLY(str) do { const char* s = (str); R(">> %s\n", quote(s)); return s; } while (0) 1842 1843const char* amodem_send( AModem modem, const char* cmd ) 1844{ 1845 const char* answer; 1846 1847 if ( modem->wait_sms != 0 ) { 1848 modem->wait_sms = 0; 1849 R( "SMS<< %s\n", quote(cmd) ); 1850 answer = handleSendSMSText( cmd, modem ); 1851 REPLY(answer); 1852 } 1853 1854 /* everything that doesn't start with 'AT' is not a command, right ? */ 1855 if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) { 1856 /* R( "-- %s\n", quote(cmd) ); */ 1857 return NULL; 1858 } 1859 R( "<< %s\n", quote(cmd) ); 1860 1861 cmd += 2; 1862 1863 /* TODO: implement command handling */ 1864 { 1865 int nn, found = 0; 1866 1867 for (nn = 0; ; nn++) { 1868 const char* scmd = sDefaultResponses[nn].cmd; 1869 1870 if (!scmd) /* end of list */ 1871 break; 1872 1873 if (scmd[0] == '!') { /* prefix match */ 1874 int len = strlen(++scmd); 1875 1876 if ( !memcmp( scmd, cmd, len ) ) { 1877 found = 1; 1878 break; 1879 } 1880 } else { /* full match */ 1881 if ( !strcmp( scmd, cmd ) ) { 1882 found = 1; 1883 break; 1884 } 1885 } 1886 } 1887 1888 if ( !found ) 1889 { 1890 D( "** UNSUPPORTED COMMAND **\n" ); 1891 REPLY( "ERROR: UNSUPPORTED" ); 1892 } 1893 else 1894 { 1895 const char* answer = sDefaultResponses[nn].answer; 1896 ResponseHandler handler = sDefaultResponses[nn].handler; 1897 1898 if ( answer != NULL ) { 1899 REPLY( amodem_printf( modem, "%s\rOK", answer ) ); 1900 } 1901 1902 if (handler == NULL) { 1903 REPLY( "OK" ); 1904 } 1905 1906 answer = handler( cmd, modem ); 1907 if (answer == NULL) 1908 REPLY( "OK" ); 1909 1910 if ( !memcmp( answer, "> ", 2 ) || 1911 !memcmp( answer, "ERROR", 5 ) || 1912 !memcmp( answer, "+CME ERROR", 6 ) ) 1913 { 1914 REPLY( answer ); 1915 } 1916 1917 if (answer != modem->out_buff) 1918 REPLY( amodem_printf( modem, "%s\rOK", answer ) ); 1919 1920 strcat( modem->out_buff, "\rOK" ); 1921 REPLY( answer ); 1922 } 1923 } 1924} 1925