Home | History | Annotate | Download | only in telephony
      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 "sim_card.h"
     13 #include <string.h>
     14 #include <assert.h>
     15 #include <stdio.h>
     16 
     17 /* set ENABLE_DYNAMIC_RECORDS to 1 to enable dynamic records
     18  * for now, this is an experimental feature that needs more testing
     19  */
     20 #define  ENABLE_DYNAMIC_RECORDS  0
     21 
     22 #define  A_SIM_PIN_SIZE  4
     23 #define  A_SIM_PUK_SIZE  8
     24 
     25 typedef struct ASimCardRec_ {
     26     ASimStatus  status;
     27     char        pin[ A_SIM_PIN_SIZE+1 ];
     28     char        puk[ A_SIM_PUK_SIZE+1 ];
     29     int         pin_retries;
     30     int         port;
     31 
     32     char        out_buff[ 256 ];
     33     int         out_size;
     34 
     35 } ASimCardRec;
     36 
     37 static ASimCardRec  _s_card[1];
     38 
     39 ASimCard
     40 asimcard_create(int port)
     41 {
     42     ASimCard  card    = _s_card;
     43     card->status      = A_SIM_STATUS_READY;
     44     card->pin_retries = 0;
     45     strncpy( card->pin, "0000", sizeof(card->pin) );
     46     strncpy( card->puk, "12345678", sizeof(card->puk) );
     47     card->port = port;
     48     return card;
     49 }
     50 
     51 void
     52 asimcard_destroy( ASimCard  card )
     53 {
     54     /* nothing really */
     55     card=card;
     56 }
     57 
     58 static __inline__ int
     59 asimcard_ready( ASimCard  card )
     60 {
     61     return card->status == A_SIM_STATUS_READY;
     62 }
     63 
     64 ASimStatus
     65 asimcard_get_status( ASimCard  sim )
     66 {
     67     return sim->status;
     68 }
     69 
     70 void
     71 asimcard_set_status( ASimCard  sim, ASimStatus  status )
     72 {
     73     sim->status = status;
     74 }
     75 
     76 const char*
     77 asimcard_get_pin( ASimCard  sim )
     78 {
     79     return sim->pin;
     80 }
     81 
     82 const char*
     83 asimcard_get_puk( ASimCard  sim )
     84 {
     85     return sim->puk;
     86 }
     87 
     88 void
     89 asimcard_set_pin( ASimCard  sim, const char*  pin )
     90 {
     91     strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
     92     sim->pin_retries = 0;
     93 }
     94 
     95 void
     96 asimcard_set_puk( ASimCard  sim, const char*  puk )
     97 {
     98     strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
     99     sim->pin_retries = 0;
    100 }
    101 
    102 
    103 int
    104 asimcard_check_pin( ASimCard  sim, const char*  pin )
    105 {
    106     if (sim->status != A_SIM_STATUS_PIN   &&
    107         sim->status != A_SIM_STATUS_READY )
    108         return 0;
    109 
    110     if ( !strcmp( sim->pin, pin ) ) {
    111         sim->status      = A_SIM_STATUS_READY;
    112         sim->pin_retries = 0;
    113         return 1;
    114     }
    115 
    116     if (sim->status != A_SIM_STATUS_READY) {
    117         if (++sim->pin_retries == 3)
    118             sim->status = A_SIM_STATUS_PUK;
    119     }
    120     return 0;
    121 }
    122 
    123 
    124 int
    125 asimcard_check_puk( ASimCard  sim, const char* puk, const char*  pin )
    126 {
    127     if (sim->status != A_SIM_STATUS_PUK)
    128         return 0;
    129 
    130     if ( !strcmp( sim->puk, puk ) ) {
    131         strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
    132         strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
    133         sim->status      = A_SIM_STATUS_READY;
    134         sim->pin_retries = 0;
    135         return 1;
    136     }
    137 
    138     if ( ++sim->pin_retries == 6 ) {
    139         sim->status = A_SIM_STATUS_ABSENT;
    140     }
    141     return 0;
    142 }
    143 
    144 typedef enum {
    145     SIM_FILE_DM = 0,
    146     SIM_FILE_DF,
    147     SIM_FILE_EF_DEDICATED,
    148     SIM_FILE_EF_LINEAR,
    149     SIM_FILE_EF_CYCLIC
    150 } SimFileType;
    151 
    152 typedef enum {
    153     SIM_FILE_READ_ONLY       = (1 << 0),
    154     SIM_FILE_NEED_PIN = (1 << 1),
    155 } SimFileFlags;
    156 
    157 /* descriptor for a known SIM File */
    158 #define  SIM_FILE_HEAD       \
    159     SimFileType     type;    \
    160     unsigned short  id;      \
    161     unsigned short  flags;
    162 
    163 typedef struct {
    164     SIM_FILE_HEAD
    165 } SimFileAnyRec, *SimFileAny;
    166 
    167 typedef struct {
    168     SIM_FILE_HEAD
    169     cbytes_t   data;
    170     int        length;
    171 } SimFileEFDedicatedRec, *SimFileEFDedicated;
    172 
    173 typedef struct {
    174     SIM_FILE_HEAD
    175     byte_t     rec_count;
    176     byte_t     rec_len;
    177     cbytes_t   records;
    178 } SimFileEFLinearRec, *SimFileEFLinear;
    179 
    180 typedef SimFileEFLinearRec   SimFileEFCyclicRec;
    181 typedef SimFileEFCyclicRec*  SimFileEFCyclic;
    182 
    183 typedef union {
    184     SimFileAnyRec          any;
    185     SimFileEFDedicatedRec  dedicated;
    186     SimFileEFLinearRec     linear;
    187     SimFileEFCyclicRec     cyclic;
    188 } SimFileRec, *SimFile;
    189 
    190 
    191 #if ENABLE_DYNAMIC_RECORDS
    192 /* convert a SIM File descriptor into an ASCII string,
    193    assumes 'dst' is NULL or properly sized.
    194    return the number of chars, or -1 on error */
    195 static int
    196 sim_file_to_hex( SimFile  file, bytes_t  dst )
    197 {
    198     SimFileType  type   = file->any.type;
    199     int          result = 0;
    200 
    201     /* see 9.2.1 in TS 51.011 */
    202     switch (type) {
    203         case SIM_FILE_EF_DEDICATED:
    204         case SIM_FILE_EF_LINEAR:
    205         case SIM_FILE_EF_CYCLIC:
    206             {
    207                 if (dst) {
    208                     int  file_size, perm;
    209 
    210                     memcpy(dst, "0000", 4);  /* bytes 1-2 are RFU */
    211                     dst += 4;
    212 
    213                     /* bytes 3-4 are the file size */
    214                     if (type == SIM_FILE_EF_DEDICATED)
    215                         file_size = file->dedicated.length;
    216                     else
    217                         file_size = file->linear.rec_count * file->linear.rec_len;
    218 
    219                     gsm_hex_from_short( dst, file_size );
    220                     dst += 4;
    221 
    222                     /* bytes 5-6 are the file id */
    223                     gsm_hex_from_short( dst, file->any.id );
    224                     dst += 4;
    225 
    226                     /* byte 7 is the file type - always EF, i.e. 0x04 */
    227                     dst[0] = '0';
    228                     dst[1] = '4';
    229                     dst   += 2;
    230 
    231                     /* byte 8 is RFU, except bit 7 for cyclic files, which indicates
    232                        that INCREASE is allowed. Since we don't support this yet... */
    233                     dst[0] = '0';
    234                     dst[1] = '0';
    235                     dst   += 2;
    236 
    237                     /* byte 9-11 are access conditions */
    238                     if (file->any.flags & SIM_FILE_READ_ONLY) {
    239                         if (file->any.flags & SIM_FILE_NEED_PIN)
    240                             perm = 0x1a;
    241                         else
    242                             perm = 0x0a;
    243                     } else {
    244                         if (file->any.flags & SIM_FILE_NEED_PIN)
    245                             perm = 0x11;
    246                         else
    247                             perm = 0x00;
    248                     }
    249                     gsm_hex_from_byte(dst, perm);
    250                     memcpy( dst+2, "a0aa", 4 );
    251                     dst += 6;
    252 
    253                     /* byte 12 is file status, we don't support invalidation */
    254                     dst[0] = '0';
    255                     dst[1] = '0';
    256                     dst   += 2;
    257 
    258                     /* byte 13 is length of the following data, always 2 */
    259                     dst[0] = '0';
    260                     dst[1] = '2';
    261                     dst   += 2;
    262 
    263                     /* byte 14 is struct of EF */
    264                     dst[0] = '0';
    265                     if (type == SIM_FILE_EF_DEDICATED)
    266                         dst[1] = '0';
    267                     else if (type == SIM_FILE_EF_LINEAR)
    268                         dst[1] = '1';
    269                     else
    270                         dst[1] = '3';
    271 
    272                     /* byte 15 is lenght of record, or 0 */
    273                     if (type == SIM_FILE_EF_DEDICATED) {
    274                         dst[0] = '0';
    275                         dst[1] = '0';
    276                     } else
    277                         gsm_hex_from_byte( dst, file->linear.rec_len );
    278                 }
    279                 result = 30;
    280             }
    281             break;
    282 
    283         default:
    284             result = -1;
    285     }
    286     return result;
    287 }
    288 
    289 
    290 static const byte_t  _const_spn_cphs[20] = {
    291     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0xff, 0xff, 0xff,
    292     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    293 };
    294 
    295 static const byte_t  _const_voicemail_cphs[1] = {
    296     0x55
    297 };
    298 
    299 static const byte_t  _const_iccid[10] = {
    300     0x98, 0x10, 0x14, 0x30, 0x12, 0x11, 0x81, 0x15, 0x70, 0x02
    301 };
    302 
    303 static const byte_t  _const_cff_cphs[1] = {
    304     0x55
    305 };
    306 
    307 static SimFileEFDedicatedRec  _const_files_dedicated[] =
    308 {
    309     { SIM_FILE_EF_DEDICATED, 0x6f14, SIM_FILE_READ_ONLY | SIM_FILE_NEED_PIN,
    310       _const_spn_cphs, sizeof(_const_spn_cphs) },
    311 
    312     { SIM_FILE_EF_DEDICATED, 0x6f11, SIM_FILE_NEED_PIN,
    313       _const_voicemail_cphs, sizeof(_const_voicemail_cphs) },
    314 
    315     { SIM_FILE_EF_DEDICATED, 0x2fe2, SIM_FILE_READ_ONLY,
    316       _const_iccid, sizeof(_const_iccid) },
    317 
    318     { SIM_FILE_EF_DEDICATED, 0x6f13, SIM_FILE_NEED_PIN,
    319       _const_cff_cphs, sizeof(_const_cff_cphs) },
    320 
    321     { 0, 0, 0, NULL, 0 }  /* end of list */
    322 };
    323 #endif /* ENABLE_DYNAMIC_RECORDS */
    324 
    325 const char*
    326 asimcard_io( ASimCard  sim, const char*  cmd )
    327 {
    328     int  nn;
    329 #if ENABLE_DYNAMIC_RECORDS
    330     int  command, id, p1, p2, p3;
    331 #endif
    332     static const struct { const char*  cmd; const char*  answer; } answers[] =
    333     {
    334         { "+CRSM=192,28436,0,0,15", "+CRSM: 144,0,000000146f1404001aa0aa01020000" },
    335         { "+CRSM=176,28436,0,0,20", "+CRSM: 144,0,416e64726f6964ffffffffffffffffffffffffff" },
    336 
    337         { "+CRSM=192,28433,0,0,15", "+CRSM: 144,0,000000016f11040011a0aa01020000" },
    338         { "+CRSM=176,28433,0,0,1", "+CRSM: 144,0,55" },
    339 
    340         { "+CRSM=192,12258,0,0,15", "+CRSM: 144,0,0000000a2fe204000fa0aa01020000" },
    341         { "+CRSM=176,12258,0,0,10", "+CRSM: 144,0,98101430121181157002" },
    342 
    343         { "+CRSM=192,28435,0,0,15", "+CRSM: 144,0,000000016f13040011a0aa01020000" },
    344         { "+CRSM=176,28435,0,0,1",  "+CRSM: 144,0,55" },
    345 
    346         { "+CRSM=192,28472,0,0,15", "+CRSM: 144,0,0000000f6f3804001aa0aa01020000" },
    347         { "+CRSM=176,28472,0,0,15", "+CRSM: 144,0,ff30ffff3c003c03000c0000f03f00" },
    348 
    349         { "+CRSM=192,28617,0,0,15", "+CRSM: 144,0,000000086fc9040011a0aa01020104" },
    350         { "+CRSM=178,28617,1,4,4",  "+CRSM: 144,0,01000000" },
    351 
    352         { "+CRSM=192,28618,0,0,15", "+CRSM: 144,0,0000000a6fca040011a0aa01020105" },
    353         { "+CRSM=178,28618,1,4,5",  "+CRSM: 144,0,0000000000" },
    354 
    355         { "+CRSM=192,28589,0,0,15", "+CRSM: 144,0,000000046fad04000aa0aa01020000" },
    356         { "+CRSM=176,28589,0,0,4",  "+CRSM: 144,0,00000003" },
    357 
    358         { "+CRSM=192,28438,0,0,15", "+CRSM: 144,0,000000026f1604001aa0aa01020000" },
    359         { "+CRSM=176,28438,0,0,2",  "+CRSM: 144,0,0233" },
    360 
    361         { "+CRSM=192,28486,0,0,15", "+CRSM: 148,4" },
    362         { "+CRSM=192,28621,0,0,15", "+CRSM: 148,4" },
    363 
    364         { "+CRSM=192,28613,0,0,15", "+CRSM: 144,0,000000f06fc504000aa0aa01020118" },
    365         { "+CRSM=178,28613,1,4,24", "+CRSM: 144,0,43058441aa890affffffffffffffffffffffffffffffffff" },
    366 
    367         { "+CRSM=192,28480,0,0,15", "+CRSM: 144,0,000000806f40040011a0aa01020120" },
    368         { "+CRSM=178,28480,1,4,32", "+CRSM: 144,0,ffffffffffffffffffffffffffffffffffff07815155258131f5ffffffffffff" },
    369 
    370         { "+CRSM=192,28615,0,0,15", "+CRSM: 144,0,000000406fc7040011a0aa01020120" },
    371         { "+CRSM=178,28615,1,4,32", "+CRSM: 144,0,566f6963656d61696cffffffffffffffffff07915155125740f9ffffffffffff" },
    372 
    373         { NULL, NULL }
    374     };
    375 
    376     assert( memcmp( cmd, "+CRSM=", 6 ) == 0 );
    377 
    378 #if ENABLE_DYNAMIC_RECORDS
    379     if ( sscanf(cmd, "+CRSM=%d,%d,%d,%d,%d", &command, &id, &p1, &p2, &p3) == 5 ) {
    380         switch (command) {
    381             case A_SIM_CMD_GET_RESPONSE:
    382                 {
    383                     const SimFileEFDedicatedRec*  file = _const_files_dedicated;
    384 
    385                     assert(p1 == 0 && p2 == 0 && p3 == 15);
    386 
    387                     for ( ; file->id != 0; file++ ) {
    388                         if (file->id == id) {
    389                             int    count;
    390                             char*  out = sim->out_buff;
    391                             strcpy( out, "+CRSM: 144,0," );
    392                             out  += strlen(out);
    393                             count = sim_file_to_hex( (SimFile) file, out );
    394                             if (count < 0)
    395                                 return "ERROR: INTERNAL SIM ERROR";
    396                             out[count] = 0;
    397                             return sim->out_buff;
    398                         }
    399                     }
    400                     break;
    401                 }
    402 
    403             case A_SIM_CMD_READ_BINARY:
    404                 {
    405                     const SimFileEFDedicatedRec*  file = _const_files_dedicated;
    406 
    407                     assert(p1 == 0 && p2 == 0);
    408 
    409                     for ( ; file->id != 0; file++ ) {
    410                         if (file->id == id) {
    411                             char*  out = sim->out_buff;
    412 
    413                             if (p3 > file->length)
    414                                 return "ERROR: BINARY LENGTH IS TOO LONG";
    415 
    416                             strcpy( out, "+CRSM: 144,0," );
    417                             out  += strlen(out);
    418                             gsm_hex_from_bytes( out, file->data, p3 );
    419                             out[p3*2] = 0;
    420                             return sim->out_buff;
    421                         }
    422                     }
    423                     break;
    424                 }
    425 
    426             case A_SIM_CMD_READ_RECORD:
    427                 break;
    428 
    429             default:
    430                 return "ERROR: UNSUPPORTED SIM COMMAND";
    431         }
    432     }
    433 #endif
    434 
    435     if (!strcmp("+CRSM=178,28480,1,4,32", cmd)) {
    436         snprintf( sim->out_buff, sizeof(sim->out_buff), "+CRSM: 144,0,ffffffffffffffffffffffffffffffffffff0781515525%d1%d%df%dffffffffffff", (sim->port / 1000) % 10, (sim->port / 10) % 10, (sim->port / 100) % 10, sim->port % 10);
    437         return sim->out_buff;
    438         }
    439 
    440     for (nn = 0; answers[nn].cmd != NULL; nn++) {
    441         if ( !strcmp( answers[nn].cmd, cmd ) ) {
    442             return answers[nn].answer;
    443         }
    444     }
    445     return "ERROR: BAD COMMAND";
    446 }
    447 
    448