• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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