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
quote(const char * line)78 static 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
111 extern AGprsNetworkType
android_parse_network_type(const char * speed)112 android_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 */
133 typedef 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 */
140 typedef 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 */
149 typedef enum {
150 A_STATUS_UNKNOWN = 0,
151 A_STATUS_AVAILABLE,
152 A_STATUS_CURRENT,
153 A_STATUS_DENIED
154 } AOperatorStatus;
155
156 typedef struct {
157 AOperatorStatus status;
158 char name[3][16];
159 } AOperatorRec, *AOperator;
160
161 typedef 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
170 typedef enum {
171 A_DATA_IP = 0,
172 A_DATA_PPP
173 } ADataType;
174
175 #define A_DATA_APN_SIZE 32
176
177 typedef 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
191 typedef 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
241 static void
amodem_unsol(AModem modem,const char * format,...)242 amodem_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
254 void
amodem_receive_sms(AModem modem,SmsPDU sms)255 amodem_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
279 static const char*
amodem_printf(AModem modem,const char * format,...)280 amodem_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
290 static void
amodem_begin_line(AModem modem)291 amodem_begin_line( AModem modem )
292 {
293 modem->out_size = 0;
294 }
295
296 static void
amodem_add_line(AModem modem,const char * format,...)297 amodem_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
307 static const char*
amodem_end_line(AModem modem)308 amodem_end_line( AModem modem )
309 {
310 modem->out_buff[ modem->out_size ] = 0;
311 return modem->out_buff;
312 }
313
314 static void
amodem_reset(AModem modem)315 amodem_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
347 static AModemRec _android_modem[1];
348
349 AModem
amodem_create(int base_port,AModemUnsolFunc unsol_func,void * unsol_opaque)350 amodem_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();
361
362 return modem;
363 }
364
365 void
amodem_set_legacy(AModem modem)366 amodem_set_legacy( AModem modem )
367 {
368 modem->supportsNetworkDataType = 0;
369 }
370
371 void
amodem_destroy(AModem modem)372 amodem_destroy( AModem modem )
373 {
374 asimcard_destroy( modem->sim );
375 modem->sim = NULL;
376 }
377
378
379 static int
amodem_has_network(AModem modem)380 amodem_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
389 ARadioState
amodem_get_radio_state(AModem modem)390 amodem_get_radio_state( AModem modem )
391 {
392 return modem->radio_state;
393 }
394
395 void
amodem_set_radio_state(AModem modem,ARadioState state)396 amodem_set_radio_state( AModem modem, ARadioState state )
397 {
398 modem->radio_state = state;
399 }
400
401 ASimCard
amodem_get_sim(AModem modem)402 amodem_get_sim( AModem modem )
403 {
404 return modem->sim;
405 }
406
407 ARegistrationState
amodem_get_voice_registration(AModem modem)408 amodem_get_voice_registration( AModem modem )
409 {
410 return modem->voice_state;
411 }
412
413 void
amodem_set_voice_registration(AModem modem,ARegistrationState state)414 amodem_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
439 ARegistrationState
amodem_get_data_registration(AModem modem)440 amodem_get_data_registration( AModem modem )
441 {
442 return modem->data_state;
443 }
444
445 void
amodem_set_data_registration(AModem modem,ARegistrationState state)446 amodem_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
473 void
amodem_set_data_network_type(AModem modem,AGprsNetworkType type)474 amodem_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
480 int
amodem_get_operator_name(AModem modem,ANameIndex index,char * buffer,int buffer_size)481 amodem_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 */
504 void
amodem_set_operator_name(AModem modem,ANameIndex index,const char * buffer,int buffer_size)505 amodem_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 **/
527 int
amodem_get_call_count(AModem modem)528 amodem_get_call_count( AModem modem )
529 {
530 return modem->call_count;
531 }
532
533 ACall
amodem_get_call(AModem modem,int index)534 amodem_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
542 static AVoiceCall
amodem_alloc_call(AModem modem)543 amodem_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
574 static void
amodem_free_call(AModem modem,AVoiceCall call)575 amodem_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
603 static AVoiceCall
amodem_find_call(AModem modem,int id)604 amodem_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
616 static void
amodem_send_calls_update(AModem modem)617 amodem_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
625 int
amodem_add_inbound_call(AModem modem,const char * number)626 amodem_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
653 ACall
amodem_find_call_by_number(AModem modem,const char * number)654 amodem_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
670 static void
acall_set_state(AVoiceCall call,ACallState state)671 acall_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
697 int
amodem_update_call(AModem modem,const char * fromNumber,ACallState state)698 amodem_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
711 int
amodem_disconnect_call(AModem modem,const char * number)712 amodem_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
727 static const char*
unknownCommand(const char * cmd,AModem modem)728 unknownCommand( 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
735 static const char*
handleRadioPower(const char * cmd,AModem modem)736 handleRadioPower( 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
751 static const char*
handleRadioPowerReq(const char * cmd,AModem modem)752 handleRadioPowerReq( 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
760 static const char*
handleSIMStatusReq(const char * cmd,AModem modem)761 handleSIMStatusReq( 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
778 static const char*
handleNetworkRegistration(const char * cmd,AModem modem)779 handleNetworkRegistration( 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
849 static const char*
handleSetDialTone(const char * cmd,AModem modem)850 handleSetDialTone( const char* cmd, AModem modem )
851 {
852 /* XXX: TODO */
853 return NULL;
854 }
855
856 static const char*
handleDeleteSMSonSIM(const char * cmd,AModem modem)857 handleDeleteSMSonSIM( const char* cmd, AModem modem )
858 {
859 /* XXX: TODO */
860 return NULL;
861 }
862
863 static const char*
handleSIM_IO(const char * cmd,AModem modem)864 handleSIM_IO( const char* cmd, AModem modem )
865 {
866 return asimcard_io( modem->sim, cmd );
867 }
868
869
870 static const char*
handleOperatorSelection(const char * cmd,AModem modem)871 handleOperatorSelection( 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 }
988 BadCommand:
989 return unknownCommand(cmd,modem);
990 }
991
992 static const char*
handleRequestOperator(const char * cmd,AModem modem)993 handleRequestOperator( 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
1009 static const char*
handleSendSMStoSIM(const char * cmd,AModem modem)1010 handleSendSMStoSIM( const char* cmd, AModem modem )
1011 {
1012 /* XXX: TODO */
1013 return "ERROR: unimplemented";
1014 }
1015
1016 static const char*
handleSendSMS(const char * cmd,AModem modem)1017 handleSendSMS( const char* cmd, AModem modem )
1018 {
1019 modem->wait_sms = 1;
1020 return "> ";
1021 }
1022
1023 #if 0
1024 static void
1025 sms_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
1045 static void
1046 smspdu_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
1082 static const char*
handleSendSMSText(const char * cmd,AModem modem)1083 handleSendSMSText( const char* cmd, AModem modem )
1084 {
1085 #if 1
1086 SmsAddressRec address;
1087 char number[16];
1088 int numlen;
1089 int len = strlen(cmd);
1090 SmsPDU pdu;
1091
1092 /* get rid of trailing escape */
1093 if (len > 0 && cmd[len-1] == 0x1a)
1094 len -= 1;
1095
1096 pdu = smspdu_create_from_hex( cmd, len );
1097 if (pdu == NULL) {
1098 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
1099 return "+CMS ERROR: INVALID SMS PDU";
1100 }
1101 if (smspdu_get_receiver_address(pdu, &address) < 0) {
1102 D("%s: could not get SMS receiver address from '%s'\n",
1103 __FUNCTION__, cmd);
1104 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
1105 }
1106
1107 do {
1108 int index;
1109
1110 numlen = sms_address_to_str( &address, number, sizeof(number) );
1111 if (numlen > sizeof(number)-1)
1112 break;
1113
1114 number[numlen] = 0;
1115 if ( remote_number_string_to_port( number ) < 0 )
1116 break;
1117
1118 if (modem->sms_receiver == NULL) {
1119 modem->sms_receiver = sms_receiver_create();
1120 if (modem->sms_receiver == NULL) {
1121 D( "%s: could not create SMS receiver\n", __FUNCTION__ );
1122 break;
1123 }
1124 }
1125
1126 index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu );
1127 if (index < 0) {
1128 D( "%s: could not add submit PDU\n", __FUNCTION__ );
1129 break;
1130 }
1131 /* the PDU is now owned by the receiver */
1132 pdu = NULL;
1133
1134 if (index > 0) {
1135 SmsAddressRec from[1];
1136 char temp[10];
1137 SmsPDU* deliver;
1138 int nn;
1139
1140 sprintf( temp, "%d", modem->base_port );
1141 sms_address_from_str( from, temp, strlen(temp) );
1142
1143 deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from );
1144 if (deliver == NULL) {
1145 D( "%s: could not create deliver PDUs for SMS index %d\n",
1146 __FUNCTION__, index );
1147 break;
1148 }
1149
1150 for (nn = 0; deliver[nn] != NULL; nn++) {
1151 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) {
1152 D( "%s: could not send SMS PDU to remote emulator\n",
1153 __FUNCTION__ );
1154 break;
1155 }
1156 }
1157
1158 smspdu_free_list(deliver);
1159 }
1160
1161 } while (0);
1162
1163 if (pdu != NULL)
1164 smspdu_free(pdu);
1165
1166 #elif 1
1167 SmsAddressRec address;
1168 char number[16];
1169 int numlen;
1170 int len = strlen(cmd);
1171 SmsPDU pdu;
1172
1173 /* get rid of trailing escape */
1174 if (len > 0 && cmd[len-1] == 0x1a)
1175 len -= 1;
1176
1177 pdu = smspdu_create_from_hex( cmd, len );
1178 if (pdu == NULL) {
1179 D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
1180 return "+CMS ERROR: INVALID SMS PDU";
1181 }
1182 if (smspdu_get_receiver_address(pdu, &address) < 0) {
1183 D("%s: could not get SMS receiver address from '%s'\n",
1184 __FUNCTION__, cmd);
1185 return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
1186 }
1187 do {
1188 numlen = sms_address_to_str( &address, number, sizeof(number) );
1189 if (numlen > sizeof(number)-1)
1190 break;
1191
1192 number[numlen] = 0;
1193 if ( remote_number_string_to_port( number ) < 0 )
1194 break;
1195
1196 if ( remote_call_sms( number, modem->base_port, pdu ) < 0 )
1197 {
1198 D("%s: could not send SMS PDU to remote emulator\n",
1199 __FUNCTION__);
1200 return "+CMS ERROR: NO EMULATOR RECEIVER";
1201 }
1202 } while (0);
1203 #else
1204 fprintf(stderr, "SMS<< %s\n", cmd);
1205 SmsPDU pdu = smspdu_create_from_hex( cmd, strlen(cmd) );
1206 if (pdu == NULL) {
1207 fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd);
1208 } else {
1209 smspdu_dump(pdu, stderr);
1210 }
1211 #endif
1212 return "+CMGS: 0\rOK\r";
1213 }
1214
1215 static const char*
handleChangeOrEnterPIN(const char * cmd,AModem modem)1216 handleChangeOrEnterPIN( const char* cmd, AModem modem )
1217 {
1218 assert( !memcmp( cmd, "+CPIN=", 6 ) );
1219 cmd += 6;
1220
1221 switch (asimcard_get_status(modem->sim)) {
1222 case A_SIM_STATUS_ABSENT:
1223 return "+CME ERROR: SIM ABSENT";
1224
1225 case A_SIM_STATUS_NOT_READY:
1226 return "+CME ERROR: SIM NOT READY";
1227
1228 case A_SIM_STATUS_READY:
1229 /* this may be a request to change the PIN */
1230 {
1231 if (strlen(cmd) == 9 && cmd[4] == ',') {
1232 char pin[5];
1233 memcpy( pin, cmd, 4 ); pin[4] = 0;
1234
1235 if ( !asimcard_check_pin( modem->sim, pin ) )
1236 return "+CME ERROR: BAD PIN";
1237
1238 memcpy( pin, cmd+5, 4 );
1239 asimcard_set_pin( modem->sim, pin );
1240 return "+CPIN: READY";
1241 }
1242 }
1243 break;
1244
1245 case A_SIM_STATUS_PIN: /* waiting for PIN */
1246 if ( asimcard_check_pin( modem->sim, cmd ) )
1247 return "+CPIN: READY";
1248 else
1249 return "+CME ERROR: BAD PIN";
1250
1251 case A_SIM_STATUS_PUK:
1252 if (strlen(cmd) == 9 && cmd[4] == ',') {
1253 char puk[5];
1254 memcpy( puk, cmd, 4 );
1255 puk[4] = 0;
1256 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) )
1257 return "+CPIN: READY";
1258 else
1259 return "+CME ERROR: BAD PUK";
1260 }
1261 return "+CME ERROR: BAD PUK";
1262
1263 default:
1264 return "+CPIN: PH-NET PIN";
1265 }
1266
1267 return "+CME ERROR: BAD FORMAT";
1268 }
1269
1270
1271 static const char*
handleListCurrentCalls(const char * cmd,AModem modem)1272 handleListCurrentCalls( const char* cmd, AModem modem )
1273 {
1274 int nn;
1275 amodem_begin_line( modem );
1276 for (nn = 0; nn < modem->call_count; nn++) {
1277 AVoiceCall vcall = modem->calls + nn;
1278 ACall call = &vcall->call;
1279 if (call->mode == A_CALL_VOICE)
1280 amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
1281 call->id, call->dir, call->state, call->mode,
1282 call->multi, call->number, 129 );
1283 }
1284 return amodem_end_line( modem );
1285 }
1286
1287 /* retrieve the current time and zone in a format suitable
1288 * for %CTZV: unsolicited message
1289 * "yy/mm/dd,hh:mm:ss(+/-)tz"
1290 * mm is 0-based
1291 * tz is in number of quarter-hours
1292 *
1293 * it seems reference-ril doesn't parse the comma (,) as anything else than a token
1294 * separator, so use a column (:) instead, the Java parsing code won't see a difference
1295 *
1296 */
1297 static const char*
handleEndOfInit(const char * cmd,AModem modem)1298 handleEndOfInit( const char* cmd, AModem modem )
1299 {
1300 time_t now = time(NULL);
1301 struct tm utc, local;
1302 long e_local, e_utc;
1303 long tzdiff;
1304 char tzname[64];
1305
1306 tzset();
1307
1308 utc = *gmtime( &now );
1309 local = *localtime( &now );
1310
1311 e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday);
1312 e_utc = utc.tm_min + 60*(utc.tm_hour + 24*utc.tm_yday);
1313
1314 if ( utc.tm_year < local.tm_year )
1315 e_local += 24*60;
1316 else if ( utc.tm_year > local.tm_year )
1317 e_utc += 24*60;
1318
1319 tzdiff = e_local - e_utc; /* timezone offset in minutes */
1320
1321 /* retrieve a zoneinfo-compatible name for the host timezone
1322 */
1323 {
1324 char* end = tzname + sizeof(tzname);
1325 char* p = bufprint_zoneinfo_timezone( tzname, end );
1326 if (p >= end)
1327 strcpy(tzname, "Unknown/Unknown");
1328
1329 /* now replace every / in the timezone name by a "!"
1330 * that's because the code that reads the CTZV line is
1331 * dumb and treats a / as a field separator...
1332 */
1333 p = tzname;
1334 while (1) {
1335 p = strchr(p, '/');
1336 if (p == NULL)
1337 break;
1338 *p = '!';
1339 p += 1;
1340 }
1341 }
1342
1343 /* as a special extension, we append the name of the host's time zone to the
1344 * string returned with %CTZ. the system should contain special code to detect
1345 * and deal with this case (since it normally relied on the operator's country code
1346 * which is hard to simulate on a general-purpose computer
1347 */
1348 return amodem_printf( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s",
1349 (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec,
1350 (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15,
1351 (local.tm_isdst > 0),
1352 tzname );
1353 }
1354
1355
1356 static const char*
handleListPDPContexts(const char * cmd,AModem modem)1357 handleListPDPContexts( const char* cmd, AModem modem )
1358 {
1359 int nn;
1360 assert( !memcmp( cmd, "+CGACT?", 7 ) );
1361 amodem_begin_line( modem );
1362 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
1363 ADataContext data = modem->data_contexts + nn;
1364 if (!data->active)
1365 continue;
1366 amodem_add_line( modem, "+CGACT: %d,%d\r", data->id, data->active );
1367 }
1368 return amodem_end_line( modem );
1369 }
1370
1371 static const char*
handleDefinePDPContext(const char * cmd,AModem modem)1372 handleDefinePDPContext( const char* cmd, AModem modem )
1373 {
1374 assert( !memcmp( cmd, "+CGDCONT=", 9 ) );
1375 cmd += 9;
1376 if (cmd[0] == '?') {
1377 int nn;
1378 amodem_begin_line(modem);
1379 for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
1380 ADataContext data = modem->data_contexts + nn;
1381 if (!data->active)
1382 continue;
1383 amodem_add_line( modem, "+CGDCONT: %d,%s,\"%s\",,0,0\r\n",
1384 data->id,
1385 data->type == A_DATA_IP ? "IP" : "PPP",
1386 data->apn );
1387 }
1388 return amodem_end_line(modem);
1389 } else {
1390 /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */
1391 int id = cmd[0] - '1';
1392 ADataType type;
1393 char apn[32];
1394 ADataContext data;
1395
1396 if ((unsigned)id > 3)
1397 goto BadCommand;
1398
1399 if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) {
1400 type = A_DATA_IP;
1401 cmd += 8;
1402 } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) {
1403 type = A_DATA_PPP;
1404 cmd += 9;
1405 } else
1406 goto BadCommand;
1407
1408 {
1409 const char* p = strchr( cmd, '"' );
1410 int len;
1411 if (p == NULL)
1412 goto BadCommand;
1413 len = (int)( p - cmd );
1414 if (len > sizeof(apn)-1 )
1415 len = sizeof(apn)-1;
1416 memcpy( apn, cmd, len );
1417 apn[len] = 0;
1418 }
1419
1420 data = modem->data_contexts + id;
1421
1422 data->id = id + 1;
1423 data->active = 1;
1424 data->type = type;
1425 memcpy( data->apn, apn, sizeof(data->apn) );
1426 }
1427 return NULL;
1428 BadCommand:
1429 return "ERROR: BAD COMMAND";
1430 }
1431
1432
1433 static const char*
handleStartPDPContext(const char * cmd,AModem modem)1434 handleStartPDPContext( const char* cmd, AModem modem )
1435 {
1436 /* XXX: TODO: handle PDP start appropriately */
1437 /* for the moment, always return success */
1438 #if 0
1439 AVoiceCall vcall = amodem_alloc_call( modem );
1440 ACall call = (ACall) vcall;
1441 if (call == NULL) {
1442 return "ERROR: TOO MANY CALLS";
1443 }
1444 call->id = 1;
1445 call->dir = A_CALL_OUTBOUND;
1446 /* XXX: it would be better to delay this */
1447 call->state = A_CALL_ACTIVE;
1448 call->mode = A_CALL_DATA;
1449 call->multi = 0;
1450 strcpy( call->number, "012345" );
1451 #endif
1452 return NULL;
1453 }
1454
1455
1456 static void
remote_voice_call_event(void * _vcall,int success)1457 remote_voice_call_event( void* _vcall, int success )
1458 {
1459 AVoiceCall vcall = _vcall;
1460 AModem modem = vcall->modem;
1461
1462 /* NOTE: success only means we could send the "gsm in new" command
1463 * to the remote emulator, nothing more */
1464
1465 if (!success) {
1466 /* aargh, the remote emulator probably quitted at that point */
1467 amodem_free_call(modem, vcall);
1468 amodem_send_calls_update(modem);
1469 }
1470 }
1471
1472
1473 static void
voice_call_event(void * _vcall)1474 voice_call_event( void* _vcall )
1475 {
1476 AVoiceCall vcall = _vcall;
1477 ACall call = &vcall->call;
1478
1479 switch (call->state) {
1480 case A_CALL_DIALING:
1481 call->state = A_CALL_ALERTING;
1482
1483 if (vcall->is_remote) {
1484 if ( remote_call_dial( call->number,
1485 vcall->modem->base_port,
1486 remote_voice_call_event, vcall ) < 0 )
1487 {
1488 /* we could not connect, probably because the corresponding
1489 * emulator is not running, so simply destroy this call.
1490 * XXX: should we send some sort of message to indicate BAD NUMBER ? */
1491 /* it seems the Android code simply waits for changes in the list */
1492 amodem_free_call( vcall->modem, vcall );
1493 }
1494 } else {
1495 /* this is not a remote emulator number, so just simulate
1496 * a small ringing delay */
1497 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT,
1498 voice_call_event, vcall );
1499 }
1500 break;
1501
1502 case A_CALL_ALERTING:
1503 call->state = A_CALL_ACTIVE;
1504 break;
1505
1506 default:
1507 assert( 0 && "unreachable event call state" );
1508 }
1509 amodem_send_calls_update(vcall->modem);
1510 }
1511
1512
1513 static const char*
handleDial(const char * cmd,AModem modem)1514 handleDial( const char* cmd, AModem modem )
1515 {
1516 AVoiceCall vcall = amodem_alloc_call( modem );
1517 ACall call = &vcall->call;
1518 int len;
1519
1520 if (call == NULL)
1521 return "ERROR: TOO MANY CALLS";
1522
1523 assert( cmd[0] == 'D' );
1524 call->dir = A_CALL_OUTBOUND;
1525 call->state = A_CALL_DIALING;
1526 call->mode = A_CALL_VOICE;
1527 call->multi = 0;
1528
1529 cmd += 1;
1530 len = strlen(cmd);
1531 if (len > 0 && cmd[len-1] == ';')
1532 len--;
1533 if (len >= sizeof(call->number))
1534 len = sizeof(call->number)-1;
1535
1536 memcpy( call->number, cmd, len );
1537 call->number[len] = 0;
1538
1539 vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
1540
1541 vcall->timer = sys_timer_create();
1542 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
1543 voice_call_event, vcall );
1544
1545 return NULL;
1546 }
1547
1548
1549 static const char*
handleAnswer(const char * cmd,AModem modem)1550 handleAnswer( const char* cmd, AModem modem )
1551 {
1552 int nn;
1553 for (nn = 0; nn < modem->call_count; nn++) {
1554 AVoiceCall vcall = modem->calls + nn;
1555 ACall call = &vcall->call;
1556
1557 if (cmd[0] == 'A') {
1558 if (call->state == A_CALL_INCOMING) {
1559 acall_set_state( vcall, A_CALL_ACTIVE );
1560 }
1561 else if (call->state == A_CALL_ACTIVE) {
1562 acall_set_state( vcall, A_CALL_HELD );
1563 }
1564 } else if (cmd[0] == 'H') {
1565 /* ATH: hangup, since user is busy */
1566 if (call->state == A_CALL_INCOMING) {
1567 amodem_free_call( modem, vcall );
1568 break;
1569 }
1570 }
1571 }
1572 return NULL;
1573 }
1574
1575 static const char*
handleHangup(const char * cmd,AModem modem)1576 handleHangup( const char* cmd, AModem modem )
1577 {
1578 if ( !memcmp(cmd, "+CHLD=", 6) ) {
1579 int nn;
1580 cmd += 6;
1581 switch (cmd[0]) {
1582 case '0': /* release all held, and set busy for waiting calls */
1583 for (nn = 0; nn < modem->call_count; nn++) {
1584 AVoiceCall vcall = modem->calls + nn;
1585 ACall call = &vcall->call;
1586 if (call->mode != A_CALL_VOICE)
1587 continue;
1588 if (call->state == A_CALL_HELD ||
1589 call->state == A_CALL_WAITING ||
1590 call->state == A_CALL_INCOMING) {
1591 amodem_free_call(modem, vcall);
1592 nn--;
1593 }
1594 }
1595 break;
1596
1597 case '1':
1598 if (cmd[1] == 0) { /* release all active, accept held one */
1599 for (nn = 0; nn < modem->call_count; nn++) {
1600 AVoiceCall vcall = modem->calls + nn;
1601 ACall call = &vcall->call;
1602 if (call->mode != A_CALL_VOICE)
1603 continue;
1604 if (call->state == A_CALL_ACTIVE) {
1605 amodem_free_call(modem, vcall);
1606 nn--;
1607 }
1608 else if (call->state == A_CALL_HELD ||
1609 call->state == A_CALL_WAITING) {
1610 acall_set_state( vcall, A_CALL_ACTIVE );
1611 }
1612 }
1613 } else { /* release specific call */
1614 int id = cmd[1] - '0';
1615 AVoiceCall vcall = amodem_find_call( modem, id );
1616 if (vcall != NULL)
1617 amodem_free_call( modem, vcall );
1618 }
1619 break;
1620
1621 case '2':
1622 if (cmd[1] == 0) { /* place all active on hold, accept held or waiting one */
1623 for (nn = 0; nn < modem->call_count; nn++) {
1624 AVoiceCall vcall = modem->calls + nn;
1625 ACall call = &vcall->call;
1626 if (call->mode != A_CALL_VOICE)
1627 continue;
1628 if (call->state == A_CALL_ACTIVE) {
1629 acall_set_state( vcall, A_CALL_HELD );
1630 }
1631 else if (call->state == A_CALL_HELD ||
1632 call->state == A_CALL_WAITING) {
1633 acall_set_state( vcall, A_CALL_ACTIVE );
1634 }
1635 }
1636 } else { /* place all active on hold, except a specific one */
1637 int id = cmd[1] - '0';
1638 for (nn = 0; nn < modem->call_count; nn++) {
1639 AVoiceCall vcall = modem->calls + nn;
1640 ACall call = &vcall->call;
1641 if (call->mode != A_CALL_VOICE)
1642 continue;
1643 if (call->state == A_CALL_ACTIVE && call->id != id) {
1644 acall_set_state( vcall, A_CALL_HELD );
1645 }
1646 }
1647 }
1648 break;
1649
1650 case '3': /* add a held call to the conversation */
1651 for (nn = 0; nn < modem->call_count; nn++) {
1652 AVoiceCall vcall = modem->calls + nn;
1653 ACall call = &vcall->call;
1654 if (call->mode != A_CALL_VOICE)
1655 continue;
1656 if (call->state == A_CALL_HELD) {
1657 acall_set_state( vcall, A_CALL_ACTIVE );
1658 break;
1659 }
1660 }
1661 break;
1662
1663 case '4': /* connect the two calls */
1664 for (nn = 0; nn < modem->call_count; nn++) {
1665 AVoiceCall vcall = modem->calls + nn;
1666 ACall call = &vcall->call;
1667 if (call->mode != A_CALL_VOICE)
1668 continue;
1669 if (call->state == A_CALL_HELD) {
1670 acall_set_state( vcall, A_CALL_ACTIVE );
1671 break;
1672 }
1673 }
1674 break;
1675 }
1676 }
1677 else
1678 return "ERROR: BAD COMMAND";
1679
1680 return NULL;
1681 }
1682
1683
1684 /* a function used to deal with a non-trivial request */
1685 typedef const char* (*ResponseHandler)(const char* cmd, AModem modem);
1686
1687 static const struct {
1688 const char* cmd; /* command coming from libreference-ril.so, if first
1689 character is '!', then the rest is a prefix only */
1690
1691 const char* answer; /* default answer, NULL if needs specific handling or
1692 if OK is good enough */
1693
1694 ResponseHandler handler; /* specific handler, ignored if 'answer' is not NULL,
1695 NULL if OK is good enough */
1696 } sDefaultResponses[] =
1697 {
1698 /* see onRadioPowerOn() */
1699 { "%CPHS=1", NULL, NULL },
1700 { "%CTZV=1", NULL, NULL },
1701
1702 /* see onSIMReady() */
1703 { "+CSMS=1", "+CSMS: 1, 1, 1", NULL },
1704 { "+CNMI=1,2,2,1,1", NULL, NULL },
1705
1706 /* see requestRadioPower() */
1707 { "+CFUN=0", NULL, handleRadioPower },
1708 { "+CFUN=1", NULL, handleRadioPower },
1709
1710 /* see requestOrSendPDPContextList() */
1711 { "+CGACT?", "", handleListPDPContexts },
1712
1713 /* see requestOperator() */
1714 { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator },
1715
1716 /* see requestQueryNetworkSelectionMode() */
1717 { "!+COPS", NULL, handleOperatorSelection },
1718
1719 /* see requestGetCurrentCalls() */
1720 { "+CLCC", NULL, handleListCurrentCalls },
1721
1722 /* see requestWriteSmsToSim() */
1723 { "!+CMGW=", NULL, handleSendSMStoSIM },
1724
1725 /* see requestHangup() */
1726 { "!+CHLD=", NULL, handleHangup },
1727
1728 /* see requestSignalStrength() */
1729 { "+CSQ", "+CSQ: 7,99", NULL }, /* XXX: TODO: implement variable signal strength and error rates */
1730
1731 /* see requestRegistrationState() */
1732 { "!+CREG", NULL, handleNetworkRegistration },
1733 { "!+CGREG", NULL, handleNetworkRegistration },
1734
1735 /* see requestSendSMS() */
1736 { "!+CMGS=", NULL, handleSendSMS },
1737
1738 /* see requestSetupDefaultPDP() */
1739 { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL },
1740 { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL },
1741
1742 { "!+CGDCONT=", NULL, handleDefinePDPContext },
1743
1744 { "+CGQREQ=1", NULL, NULL },
1745 { "+CGQMIN=1", NULL, NULL },
1746 { "+CGEREP=1,0", NULL, NULL },
1747 { "+CGACT=1,0", NULL, NULL },
1748 { "D*99***1#", NULL, handleStartPDPContext },
1749
1750 /* see requestDial() */
1751 { "!D", NULL, handleDial }, /* the code says that success/error is ignored, the call state will
1752 be polled through +CLCC instead */
1753
1754 /* see requestSMSAcknowledge() */
1755 { "+CNMA=1", NULL, NULL },
1756 { "+CNMA=2", NULL, NULL },
1757
1758 /* see requestSIM_IO() */
1759 { "!+CRSM=", NULL, handleSIM_IO },
1760
1761 /* see onRequest() */
1762 { "+CHLD=0", NULL, handleHangup },
1763 { "+CHLD=1", NULL, handleHangup },
1764 { "+CHLD=2", NULL, handleHangup },
1765 { "+CHLD=3", NULL, handleHangup },
1766 { "A", NULL, handleAnswer }, /* answer the call */
1767 { "H", NULL, handleAnswer }, /* user is busy */
1768 { "!+VTS=", NULL, handleSetDialTone },
1769 { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL }, /* request internation subscriber identification number */
1770 { "+CGSN", "000000000000000", NULL }, /* request model version */
1771 { "+CUSD=2",NULL, NULL }, /* Cancel USSD */
1772 { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */
1773 { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */
1774 { "!+CPIN=", NULL, handleChangeOrEnterPIN },
1775
1776 /* see getSIMStatus() */
1777 { "+CPIN?", NULL, handleSIMStatusReq },
1778 { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL },
1779
1780 /* see isRadioOn() */
1781 { "+CFUN?", NULL, handleRadioPowerReq },
1782
1783 /* see initializeCallback() */
1784 { "E0Q0V1", NULL, NULL },
1785 { "S0=0", NULL, NULL },
1786 { "+CMEE=1", NULL, NULL },
1787 { "+CREG=2", NULL, handleNetworkRegistration },
1788 { "+CREG=1", NULL, handleNetworkRegistration },
1789 { "+CGREG=1", NULL, handleNetworkRegistration },
1790 { "+CCWA=1", NULL, NULL },
1791 { "+CMOD=0", NULL, NULL },
1792 { "+CMUT=0", NULL, NULL },
1793 { "+CSSN=0,1", NULL, NULL },
1794 { "+COLP=0", NULL, NULL },
1795 { "+CSCS=\"HEX\"", NULL, NULL },
1796 { "+CUSD=1", NULL, NULL },
1797 { "+CGEREP=1,0", NULL, NULL },
1798 { "+CMGF=0", NULL, handleEndOfInit }, /* now is a goof time to send the current tme and timezone */
1799 { "%CPI=3", NULL, NULL },
1800 { "%CSTAT=1", NULL, NULL },
1801
1802 /* end of list */
1803 {NULL, NULL, NULL}
1804 };
1805
1806
1807 #define REPLY(str) do { const char* s = (str); R(">> %s\n", quote(s)); return s; } while (0)
1808
amodem_send(AModem modem,const char * cmd)1809 const char* amodem_send( AModem modem, const char* cmd )
1810 {
1811 const char* answer;
1812
1813 if ( modem->wait_sms != 0 ) {
1814 modem->wait_sms = 0;
1815 R( "SMS<< %s\n", quote(cmd) );
1816 answer = handleSendSMSText( cmd, modem );
1817 REPLY(answer);
1818 }
1819
1820 /* everything that doesn't start with 'AT' is not a command, right ? */
1821 if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) {
1822 /* R( "-- %s\n", quote(cmd) ); */
1823 return NULL;
1824 }
1825 R( "<< %s\n", quote(cmd) );
1826
1827 cmd += 2;
1828
1829 /* TODO: implement command handling */
1830 {
1831 int nn, found = 0;
1832
1833 for (nn = 0; ; nn++) {
1834 const char* scmd = sDefaultResponses[nn].cmd;
1835
1836 if (!scmd) /* end of list */
1837 break;
1838
1839 if (scmd[0] == '!') { /* prefix match */
1840 int len = strlen(++scmd);
1841
1842 if ( !memcmp( scmd, cmd, len ) ) {
1843 found = 1;
1844 break;
1845 }
1846 } else { /* full match */
1847 if ( !strcmp( scmd, cmd ) ) {
1848 found = 1;
1849 break;
1850 }
1851 }
1852 }
1853
1854 if ( !found )
1855 {
1856 D( "** UNSUPPORTED COMMAND **\n" );
1857 REPLY( "ERROR: UNSUPPORTED" );
1858 }
1859 else
1860 {
1861 const char* answer = sDefaultResponses[nn].answer;
1862 ResponseHandler handler = sDefaultResponses[nn].handler;
1863
1864 if ( answer != NULL ) {
1865 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
1866 }
1867
1868 if (handler == NULL) {
1869 REPLY( "OK" );
1870 }
1871
1872 answer = handler( cmd, modem );
1873 if (answer == NULL)
1874 REPLY( "OK" );
1875
1876 if ( !memcmp( answer, "> ", 2 ) ||
1877 !memcmp( answer, "ERROR", 5 ) ||
1878 !memcmp( answer, "+CME ERROR", 6 ) )
1879 {
1880 REPLY( answer );
1881 }
1882
1883 if (answer != modem->out_buff)
1884 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
1885
1886 strcat( modem->out_buff, "\rOK" );
1887 REPLY( answer );
1888 }
1889 }
1890 }
1891