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