diff options
Diffstat (limited to 'telephony/sim_card.c')
-rw-r--r-- | telephony/sim_card.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/telephony/sim_card.c b/telephony/sim_card.c new file mode 100644 index 0000000..9e48200 --- /dev/null +++ b/telephony/sim_card.c @@ -0,0 +1,432 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "sim_card.h" +#include <string.h> +#include <assert.h> + +#define A_SIM_PIN_SIZE 4 +#define A_SIM_PUK_SIZE 8 + +typedef struct ASimCardRec_ { + ASimStatus status; + char pin[ A_SIM_PIN_SIZE+1 ]; + char puk[ A_SIM_PUK_SIZE+1 ]; + int pin_retries; + + char out_buff[ 256 ]; + int out_size; + +} ASimCardRec; + +static ASimCardRec _s_card[1]; + +ASimCard +asimcard_create( void ) +{ + ASimCard card = _s_card; + card->status = A_SIM_STATUS_READY; + card->pin_retries = 0; + strncpy( card->pin, "0000", sizeof(card->pin) ); + strncpy( card->puk, "12345678", sizeof(card->puk) ); + return card; +} + +void +asimcard_destroy( ASimCard card ) +{ + /* nothing really */ + card=card; +} + +static __inline__ int +asimcard_ready( ASimCard card ) +{ + return card->status == A_SIM_STATUS_READY; +} + +ASimStatus +asimcard_get_status( ASimCard sim ) +{ + return sim->status; +} + +void +asimcard_set_status( ASimCard sim, ASimStatus status ) +{ + sim->status = status; +} + +const char* +asimcard_get_pin( ASimCard sim ) +{ + return sim->pin; +} + +const char* +asimcard_get_puk( ASimCard sim ) +{ + return sim->puk; +} + +void +asimcard_set_pin( ASimCard sim, const char* pin ) +{ + strncpy( sim->pin, pin, A_SIM_PIN_SIZE ); + sim->pin_retries = 0; +} + +void +asimcard_set_puk( ASimCard sim, const char* puk ) +{ + strncpy( sim->puk, puk, A_SIM_PUK_SIZE ); + sim->pin_retries = 0; +} + + +int +asimcard_check_pin( ASimCard sim, const char* pin ) +{ + if (sim->status != A_SIM_STATUS_PIN && + sim->status != A_SIM_STATUS_READY ) + return 0; + + if ( !strcmp( sim->pin, pin ) ) { + sim->status = A_SIM_STATUS_READY; + sim->pin_retries = 0; + return 1; + } + + if (sim->status != A_SIM_STATUS_READY) { + if (++sim->pin_retries == 3) + sim->status = A_SIM_STATUS_PUK; + } + return 0; +} + + +int +asimcard_check_puk( ASimCard sim, const char* puk, const char* pin ) +{ + if (sim->status != A_SIM_STATUS_PUK) + return 0; + + if ( !strcmp( sim->puk, puk ) ) { + strncpy( sim->puk, puk, A_SIM_PUK_SIZE ); + strncpy( sim->pin, pin, A_SIM_PIN_SIZE ); + sim->status = A_SIM_STATUS_READY; + sim->pin_retries = 0; + return 1; + } + + if ( ++sim->pin_retries == 6 ) { + sim->status = A_SIM_STATUS_ABSENT; + } + return 0; +} + +typedef enum { + SIM_FILE_DM = 0, + SIM_FILE_DF, + SIM_FILE_EF_DEDICATED, + SIM_FILE_EF_LINEAR, + SIM_FILE_EF_CYCLIC +} SimFileType; + +typedef enum { + SIM_FILE_READ_ONLY = (1 << 0), + SIM_FILE_NEED_PIN = (1 << 1), +} SimFileFlags; + +/* descriptor for a known SIM File */ +#define SIM_FILE_HEAD \ + SimFileType type; \ + unsigned short id; \ + unsigned short flags; + +typedef struct { + SIM_FILE_HEAD +} SimFileAnyRec, *SimFileAny; + +typedef struct { + SIM_FILE_HEAD + cbytes_t data; + int length; +} SimFileEFDedicatedRec, *SimFileEFDedicated; + +typedef struct { + SIM_FILE_HEAD + byte_t rec_count; + byte_t rec_len; + cbytes_t records; +} SimFileEFLinearRec, *SimFileEFLinear; + +typedef SimFileEFLinearRec SimFileEFCyclicRec; +typedef SimFileEFCyclicRec* SimFileEFCyclic; + +typedef union { + SimFileAnyRec any; + SimFileEFDedicatedRec dedicated; + SimFileEFLinearRec linear; + SimFileEFCyclicRec cyclic; +} SimFileRec, *SimFile; + + +/* convert a SIM File descriptor into an ASCII string, + assumes 'dst' is NULL or properly sized. + return the number of chars, or -1 on error */ +static int +sim_file_to_hex( SimFile file, bytes_t dst ) +{ + SimFileType type = file->any.type; + int result = 0; + + /* see 9.2.1 in TS 51.011 */ + switch (type) { + case SIM_FILE_EF_DEDICATED: + case SIM_FILE_EF_LINEAR: + case SIM_FILE_EF_CYCLIC: + { + if (dst) { + int file_size, file_type, perm; + + memcpy(dst, "0000", 4); /* bytes 1-2 are RFU */ + dst += 4; + + /* bytes 3-4 are the file size */ + if (type == SIM_FILE_EF_DEDICATED) + file_size = file->dedicated.length; + else + file_size = file->linear.rec_count * file->linear.rec_len; + + gsm_hex_from_short( dst, file_size ); + dst += 4; + + /* bytes 5-6 are the file id */ + gsm_hex_from_short( dst, file->any.id ); + dst += 4; + + /* byte 7 is the file type - always EF, i.e. 0x04 */ + dst[0] = '0'; + dst[1] = '4'; + dst += 2; + + /* byte 8 is RFU, except bit 7 for cyclic files, which indicates + that INCREASE is allowed. Since we don't support this yet... */ + dst[0] = '0'; + dst[1] = '0'; + dst += 2; + + /* byte 9-11 are access conditions */ + if (file->any.flags & SIM_FILE_READ_ONLY) { + if (file->any.flags & SIM_FILE_NEED_PIN) + perm = 0x1a; + else + perm = 0x0a; + } else { + if (file->any.flags & SIM_FILE_NEED_PIN) + perm = 0x11; + else + perm = 0x00; + } + gsm_hex_from_byte(dst, perm); + memcpy( dst+2, "a0aa", 4 ); + dst += 6; + + /* byte 12 is file status, we don't support invalidation */ + dst[0] = '0'; + dst[1] = '0'; + dst += 2; + + /* byte 13 is length of the following data, always 2 */ + dst[0] = '0'; + dst[1] = '2'; + dst += 2; + + /* byte 14 is struct of EF */ + dst[0] = '0'; + if (type == SIM_FILE_EF_DEDICATED) + dst[1] = '0'; + else if (type == SIM_FILE_EF_LINEAR) + dst[1] = '1'; + else + dst[1] = '3'; + + /* byte 15 is lenght of record, or 0 */ + if (type == SIM_FILE_EF_DEDICATED) { + dst[0] = '0'; + dst[1] = '0'; + } else + gsm_hex_from_byte( dst, file->linear.rec_len ); + } + result = 30; + } + break; + + default: + result = -1; + } + return result; +} + + +static const byte_t _const_spn_cphs[20] = { + 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const byte_t _const_voicemail_cphs[1] = { + 0x55 +}; + +static const byte_t _const_iccid[10] = { + 0x98, 0x10, 0x14, 0x30, 0x12, 0x11, 0x81, 0x15, 0x70, 0x02 +}; + +static const byte_t _const_cff_cphs[1] = { + 0x55 +}; + +static SimFileEFDedicatedRec _const_files_dedicated[] = +{ + { SIM_FILE_EF_DEDICATED, 0x6f14, SIM_FILE_READ_ONLY | SIM_FILE_NEED_PIN, + _const_spn_cphs, sizeof(_const_spn_cphs) }, + + { SIM_FILE_EF_DEDICATED, 0x6f11, SIM_FILE_NEED_PIN, + _const_voicemail_cphs, sizeof(_const_voicemail_cphs) }, + + { SIM_FILE_EF_DEDICATED, 0x2fe2, SIM_FILE_READ_ONLY, + _const_iccid, sizeof(_const_iccid) }, + + { SIM_FILE_EF_DEDICATED, 0x6f13, SIM_FILE_NEED_PIN, + _const_cff_cphs, sizeof(_const_cff_cphs) }, + + { 0, 0, 0, NULL, 0 } /* end of list */ +}; + + +const char* +asimcard_io( ASimCard sim, const char* cmd ) +{ + int nn; + int command, id, p1, p2, p3; + + static const struct { const char* cmd; const char* answer; } answers[] = + { + { "+CRSM=192,28436,0,0,15", "+CRSM: 144,0,000000146f1404001aa0aa01020000" }, + { "+CRSM=176,28436,0,0,20", "+CRSM: 144,0,416e64726f6964ffffffffffffffffffffffffff" }, + + { "+CRSM=192,28433,0,0,15", "+CRSM: 144,0,000000016f11040011a0aa01020000" }, + { "+CRSM=176,28433,0,0,1", "+CRSM: 144,0,55" }, + + { "+CRSM=192,12258,0,0,15", "+CRSM: 144,0,0000000a2fe204000fa0aa01020000" }, + { "+CRSM=176,12258,0,0,10", "+CRSM: 144,0,98101430121181157002" }, + + { "+CRSM=192,28435,0,0,15", "+CRSM: 144,0,000000016f13040011a0aa01020000" }, + { "+CRSM=176,28435,0,0,1", "+CRSM: 144,0,55" }, + + { "+CRSM=192,28472,0,0,15", "+CRSM: 144,0,0000000f6f3804001aa0aa01020000" }, + { "+CRSM=176,28472,0,0,15", "+CRSM: 144,0,ff30ffff3c003c03000c0000f03f00" }, + + { "+CRSM=192,28617,0,0,15", "+CRSM: 144,0,000000086fc9040011a0aa01020104" }, + { "+CRSM=178,28617,1,4,4", "+CRSM: 144,0,01000000" }, + + { "+CRSM=192,28618,0,0,15", "+CRSM: 144,0,0000000a6fca040011a0aa01020105" }, + { "+CRSM=178,28618,1,4,5", "+CRSM: 144,0,0000000000" }, + + { "+CRSM=192,28589,0,0,15", "+CRSM: 144,0,000000046fad04000aa0aa01020000" }, + { "+CRSM=176,28589,0,0,4", "+CRSM: 144,0,00000003" }, + + { "+CRSM=192,28438,0,0,15", "+CRSM: 144,0,000000026f1604001aa0aa01020000" }, + { "+CRSM=176,28438,0,0,2", "+CRSM: 144,0,0233" }, + + { "+CRSM=192,28486,0,0,15", "+CRSM: 148,4" }, + { "+CRSM=192,28621,0,0,15", "+CRSM: 148,4" }, + + { "+CRSM=192,28613,0,0,15", "+CRSM: 144,0,000000f06fc504000aa0aa01020118" }, + { "+CRSM=178,28613,1,4,24", "+CRSM: 144,0,43058441aa890affffffffffffffffffffffffffffffffff" }, + + { "+CRSM=192,28480,0,0,15", "+CRSM: 144,0,000000806f40040011a0aa01020120" }, + { "+CRSM=178,28480,1,4,32", "+CRSM: 144,0,ffffffffffffffffffffffffffffffffffff07815155258131f5ffffffffffff" }, + + { "+CRSM=192,28615,0,0,15", "+CRSM: 144,0,000000406fc7040011a0aa01020120" }, + { "+CRSM=178,28615,1,4,32", "+CRSM: 144,0,566f6963656d61696cffffffffffffffffff07915155125740f9ffffffffffff" }, + + { NULL, NULL } + }; + + assert( memcmp( cmd, "+CRSM=", 6 ) == 0 ); + +#if 0 /* this code officially disabled in the depot until properly tested and debugged */ + if ( sscanf(cmd, "+CRSM=%d,%d,%d,%d,%d", &command, &id, &p1, &p2, &p3) == 5 ) { + switch (command) { + case A_SIM_CMD_GET_RESPONSE: + { + const SimFileEFDedicatedRec* file = _const_files_dedicated; + + assert(p1 == 0 && p2 == 0 && p3 == 15); + + for ( ; file->id != 0; file++ ) { + if (file->id == id) { + int count; + char* out = sim->out_buff; + strcpy( out, "+CRSM: 144,0," ); + out += strlen(out); + count = sim_file_to_hex( (SimFile) file, out ); + if (count < 0) + return "ERROR: INTERNAL SIM ERROR"; + out[count] = 0; + return sim->out_buff; + } + } + break; + } + + case A_SIM_CMD_READ_BINARY: + { + const SimFileEFDedicatedRec* file = _const_files_dedicated; + + assert(p1 == 0 && p2 == 0); + + for ( ; file->id != 0; file++ ) { + if (file->id == id) { + char* out = sim->out_buff; + + if (p3 > file->length) + return "ERROR: BINARY LENGTH IS TOO LONG"; + + strcpy( out, "+CRSM: 144,0," ); + out += strlen(out); + gsm_hex_from_bytes( out, file->data, p3 ); + out[p3*2] = 0; + return sim->out_buff; + } + } + break; + } + + case A_SIM_CMD_READ_RECORD: + break; + + default: + return "ERROR: UNSUPPORTED SIM COMMAND"; + } + } +#endif + + for (nn = 0; answers[nn].cmd != NULL; nn++) { + if ( !strcmp( answers[nn].cmd, cmd ) ) { + return answers[nn].answer; + } + } + return "ERROR: BAD COMMAND"; +} + |