From cd59e3afac8019d0d45e5e8247776b32e01dcf72 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Wed, 22 Sep 2010 21:19:42 +0200 Subject: [PATCH] [WIP] target/fw/sim: SIM Layer 1 driver FIXME need cleaning ! Written-by: dexter [FIXME: name / email ?] Signed-off-by: Sylvain Munaut --- src/target/firmware/apps/layer1/main.c | 122 ++++++++++++++++++++ src/target/firmware/apps/simtest/main.c | 71 +++++++++++- src/target/firmware/calypso/sim.c | 130 ++++++++++++++++------ src/target/firmware/include/calypso/sim.h | 4 +- src/target/firmware/layer1/l23_api.c | 25 +++++ 5 files changed, 309 insertions(+), 43 deletions(-) mode change 100755 => 100644 src/target/firmware/calypso/sim.c diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c index d918145..749dde4 100644 --- a/src/target/firmware/apps/layer1/main.c +++ b/src/target/firmware/apps/layer1/main.c @@ -48,12 +48,122 @@ const char *hr = "======================================================================\n"; +/* SIM Stuff, TODO: clean it up */ + +#include + +#include + +#define SIM_CLASS 0xA0 /* Class that contains the following instructions */ +#define SIM_GET_RESPONSE 0xC0 /* Get the response of a command from the card */ +#define SIM_READ_BINARY 0xB0 /* Read file in binary mode */ + +#define L3_MSG_HEAD 4 + +static uint8_t sim_data[256]; /* buffer for SIM command */ +static volatile uint16_t sim_len = 0; /* lenght of data in sim_data[] */ + +void sim_apdu(uint16_t len, uint8_t *data) +{ + memcpy(sim_data, data, len); + sim_len = len; +} + +/* allocate a large enough buffer for the SIM response */ + +struct msgb *my_l1ctl_msgb_alloc(uint8_t msg_type) +{ + struct msgb *msg; + struct l1ctl_hdr *l1h; + + msg = msgb_alloc_headroom(256, L3_MSG_HEAD, "l1ctl1"); + if (!msg) { + while (1) { + puts("OOPS. Out of buffers...\n"); + } + + return NULL; + } + l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h)); + l1h->msg_type = msg_type; + l1h->flags = 0; + + msg->l1h = (uint8_t *)l1h; + + return msg; +} + +static void sim_handler(void) +{ + uint8_t status_word[2]; + struct msgb *msg; + uint8_t *dat; + uint16_t length; + + if(sim_len) /* a new SIM command has arrived */ + { + status_word[0] = 0; + status_word[1] = 0; + + msg = my_l1ctl_msgb_alloc(L1CTL_SIM_CONF); + + /* check if instructions expects a response (TODO: add more instructions */ + if (/* GET RESPONSE needs SIM_APDU_GET */ + (sim_len == 5 && sim_data[0] == SIM_CLASS && + sim_data[1] == SIM_GET_RESPONSE && sim_data[2] == 0x00 && + sim_data[3] == 0x00) || + /* READ BINARY needs SIM_APDU_GET */ + (sim_len >= 5 && sim_data[0] == SIM_CLASS && + sim_data[1] == SIM_READ_BINARY)) + { + /* allocate space for expected response */ + length = sim_data[4]; + dat = msgb_put(msg, length + 2); + + if(calypso_sim_transceive(sim_data[0], sim_data[1], sim_data[2], sim_data[3], sim_data[4], dat, status_word, SIM_APDU_GET) != 0) + puts("SIM ERROR !\n"); + printf("Status 1: %02X %02X\n", status_word[0], status_word[1]); + + /* copy status at the end */ + memcpy(dat + length, status_word, 2); + + l1_queue_for_l2(msg); + } + else + { + if(calypso_sim_transceive(sim_data[0], sim_data[1], sim_data[2], sim_data[3], sim_data[4], &sim_data[5], status_word, SIM_APDU_PUT) != 0) + puts("SIM ERROR !\n"); + printf("Status 2: %02X %02X\n", status_word[0], status_word[1]); + + /* 2 bytes status */ + length = 2; + dat = msgb_put(msg, length); + memcpy(dat, status_word, length); + + l1_queue_for_l2(msg); + } + + sim_len = 0; + } +} + /* MAIN program **************************************************************/ static void key_handler(enum key_codes code, enum key_states state); +/* called while waiting for SIM */ + +void sim_wait_handler(void) +{ + l1a_compl_execute(); + update_timers(); +} + int main(void) { + uint8_t atr[20]; + uint8_t atrLength = 0; + board_init(); puts("\n\nOSMOCOM Layer 1 (revision " GIT_REVISION ")\n"); @@ -72,6 +182,14 @@ int main(void) display_set_attr(DISP_ATTR_INVERT); display_puts("layer1.bin"); + /* initialize SIM */ + calypso_sim_init(sim_wait_handler); + + puts("Power up simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_powerup(atr); + + layer1_init(); tpu_frame_irq_en(1, 1); @@ -79,6 +197,7 @@ int main(void) while (1) { l1a_compl_execute(); update_timers(); + sim_handler(); } /* NOT REACHED */ @@ -156,6 +275,9 @@ static void key_handler(enum key_codes code, enum key_states state) default: break; } + /* power down SIM, TODO: this will happen with every key pressed, + put it somewhere else ! */ + calypso_sim_powerdown(); } diff --git a/src/target/firmware/apps/simtest/main.c b/src/target/firmware/apps/simtest/main.c index 83f708e..4b9fbcd 100755 --- a/src/target/firmware/apps/simtest/main.c +++ b/src/target/firmware/apps/simtest/main.c @@ -52,14 +52,14 @@ static void myHexdump(uint8_t *data, int len) int i; for(i=0;i Status word: %x\n", (status_word[0] << 8) | status_word[1]); + + if(status_word[0] != 0x9F || status_word[1] != 0x0C) + return (status_word[0] << 8) | status_word[1]; + + /* GET RESPONSE */ + + if(calypso_sim_transceive(SIM_CLASS, SIM_GET_RESPONSE, 0, 0, 0x0C, data ,status_word, SIM_APDU_GET) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + for (i= 0; i < ms*1300; i++) { i; } +} + /* Execute my (dexter's) personal test */ void do_sim_test(void) { @@ -171,7 +220,7 @@ void do_sim_test(void) uint8_t atr[20]; uint8_t atrLength = 0; - + memset(atr,0,sizeof(atr)); @@ -185,7 +234,7 @@ void do_sim_test(void) /* Initialize Sim-Controller driver */ puts("Initializing driver:\n"); - calypso_sim_init(); + calypso_sim_init(NULL); /* Power up sim and display ATR */ puts("Power up simcard:\n"); @@ -215,6 +264,9 @@ void do_sim_test(void) puts(" * Testing SELECT: Selecting DF_GSM\n"); printf(" ==> Status word: %x\n", sim_select(SIM_DF_GSM)); + puts(" * Testing PIN VERIFY\n"); + printf(" ==> Status word: %x\n", sim_verify("1234")); + puts(" * Testing SELECT: Selecting EF_IMSI\n"); printf(" ==> Status word: %x\n", sim_select(SIM_EF_IMSI)); @@ -222,11 +274,18 @@ void do_sim_test(void) printf(" ==> Status word: %x\n", sim_status()); memset(buffer,0,sizeof(buffer)); - puts(" * Testing READ BINARY:\n"); + puts(" * Testing READ BINARY:\n"); printf(" ==> Status word: %x\n", sim_readbinary(0,0,9,buffer)); printf(" Data: "); myHexdump(buffer,9); + memset(buffer,0,sizeof(buffer)); + memcpy(buffer,"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff",16); + puts(" * Testing RUN GSM ALGORITHM\n"); + printf(" ==> Status word: %x\n", sim_run_gsm_algorith(buffer)); + printf(" Result: "); + myHexdump(buffer,12); + delay_ms(5000); calypso_sim_powerdown(); diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c old mode 100755 new mode 100644 index a539cf8..214d689 --- a/src/target/firmware/calypso/sim.c +++ b/src/target/firmware/calypso/sim.c @@ -29,9 +29,12 @@ #include #include -static int sim_rx_character_count = 0; /* How many bytes have been received by calypso_sim_receive() */ -static int sim_tx_character_count = 0; /* How many bytes have been transmitted by calypso_sim_transmit() */ -static int sim_tx_character_length = 0; /* How many bytes have to be transmitted by calypso_sim_transmit() */ +static void (*sim_wait_cb)(void) = NULL; /* called during waiting for SIM */ +static volatile uint8_t sim_ignore_waiting_char = 0; /* signal ignoring of NULL procedure byte */ +static volatile int sim_rx_character_count = 0; /* How many bytes have been received by calypso_sim_receive() */ +static volatile int sim_rx_max_character_count = 0; /* How many bytes have been received by calypso_sim_receive() */ +static volatile int sim_tx_character_count = 0; /* How many bytes have been transmitted by calypso_sim_transmit() */ +static volatile int sim_tx_character_length = 0; /* How many bytes have to be transmitted by calypso_sim_transmit() */ static uint8_t *rx_buffer = 0; /* RX-Buffer that is issued by calypso_sim_receive() */ static uint8_t *tx_buffer = 0; /* TX-Buffer that is issued by calypso_sim_transmit() */ volatile static int rxDoneFlag = 0; /* Used for rx synchronization instead of a semaphore in calypso_sim_receive() */ @@ -149,25 +152,25 @@ void calypso_sim_regdump(void) else puts(" | |-REG_SIM_CONF1_CONFSCLKDIV = 0 ==> SIM clock frequency is 13/4 Mhz.\n"); delay_ms(SIM_DEBUG_OUTPUTDELAY); - + if(regVal & REG_SIM_CONF1_CONFSCLKLEV) puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 1 ==> SIM clock idle level is high.\n"); else puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 0 ==> SIM clock idle level is low.\n"); delay_ms(SIM_DEBUG_OUTPUTDELAY); - + if(regVal & REG_SIM_CONF1_CONFETUPERIOD) puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 1 ==> ETU period is 512/8*1/Fsclk.\n"); else puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 0 ==> ETU period is 372/8*1/Fsclk.\n"); delay_ms(SIM_DEBUG_OUTPUTDELAY); - + if(regVal & REG_SIM_CONF1_CONFBYPASS) puts(" | |-REG_SIM_CONF1_CONFBYPASS = 1 ==> Hardware timers and start and stop sequences are bypassed.\n"); else puts(" | |-REG_SIM_CONF1_CONFBYPASS = 0 ==> Hardware timers and start and stop sequences are normal.\n"); delay_ms(SIM_DEBUG_OUTPUTDELAY); - + if(regVal & REG_SIM_CONF1_CONFSVCCLEV) puts(" | |-REG_SIM_CONF1_CONFSVCCLEV = 1 ==> SVCC Level is high (Only valid when CONFBYPASS = 1).\n"); else @@ -305,7 +308,11 @@ void calypso_sim_regdump(void) int calypso_sim_powerup(uint8_t *atr) { /* Enable level shifters and voltage regulator */ + #if 1 // 2.9V twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN | VRPCSIM_SIMSEL); + #else // 1.8V + twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN); + #endif #if (SIM_DEBUG == 1) puts(" * Power enabled!\n"); #endif @@ -326,7 +333,7 @@ int calypso_sim_powerup(uint8_t *atr) /* Catch ATR */ if(atr != 0) - return calypso_sim_receive(atr); + return calypso_sim_receive(atr, 0); else return 0; } @@ -383,18 +390,19 @@ int calypso_sim_reset(uint8_t *atr) /* Catch ATR */ if(atr != 0) - return calypso_sim_receive(atr); + return calypso_sim_receive(atr, 0); else return 0; } /* Receive raw data through the sim interface */ -int calypso_sim_receive(uint8_t *data) +int calypso_sim_receive(uint8_t *data, uint8_t len) { /* Prepare buffers and flags */ rx_buffer = data; sim_rx_character_count = 0; rxDoneFlag = 0; + sim_rx_max_character_count = len; /* Switch I/O direction to input */ writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); @@ -403,14 +411,17 @@ int calypso_sim_receive(uint8_t *data) writew(~(REG_SIM_MASKIT_MASK_SIM_RX | REG_SIM_MASKIT_MASK_SIM_WT), REG_SIM_MASKIT); /* Wait till rxDoneFlag is set */ - while(rxDoneFlag == 0); + while(rxDoneFlag == 0) + { + if(sim_wait_cb) (*sim_wait_cb)(); + } /* Disable all interrupt driven functions by masking all interrupts */ writew(0xFF, REG_SIM_MASKIT); /* Hand back the number of bytes received */ return sim_rx_character_count; - + return; } @@ -434,8 +445,11 @@ int calypso_sim_transmit(uint8_t *data, int length) tx_buffer++; sim_tx_character_count++; - /* Wait till rxDoneFlag is set */ - while(txDoneFlag == 0); + /* Wait till xDoneFlag is set */ + while(txDoneFlag == 0) + { + if(sim_wait_cb) (*sim_wait_cb)(); + } /* Disable all interrupt driven functions by masking all interrupts */ writew(0xFF, REG_SIM_MASKIT); @@ -452,7 +466,7 @@ void sim_irq_handler(enum irq_nr irq) /* Display interrupt information */ #if (SIM_DEBUG == 1) - puts("SIM-ISR: Interrupt caught: "); + puts("SIM-ISR: "); #endif if(regVal & REG_SIM_IT_SIM_NATR) { @@ -481,30 +495,63 @@ void sim_irq_handler(enum irq_nr irq) if(regVal & REG_SIM_IT_SIM_TX) { #if (SIM_DEBUG == 1) - puts(" Waiting for character to transmit...\n"); + puts(" Waiting for transmit...\n"); #endif if(sim_tx_character_count >= sim_tx_character_length) + { txDoneFlag = 1; + } else { writew(*tx_buffer,REG_SIM_DTX); tx_buffer++; sim_tx_character_count++; + + #if 1 /* Dieter: set to 0 to get problems with some cards */ + /* its essential to immediately switch to RX after TX is done */ + if(sim_tx_character_count >= sim_tx_character_length) + { + /* TODO: set a proper delay here, 4 is to + long if not debugging and no delay is too short */ + delay_ms(1); + /* Switch I/O direction to input */ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); + } + #endif } } /* Used by: calypso_sim_receive() to receive the incoming data */ if(regVal & REG_SIM_IT_SIM_RX) { + uint8_t ch = (uint8_t) (readw(REG_SIM_DRX) & 0xFF); + /* ignore NULL procedure byte */ + if(ch == 0x60 && sim_ignore_waiting_char) + { #if (SIM_DEBUG == 1) - puts(" Waiting characters to be read...\n"); + puts(" 0x60 received...\n"); +#endif + return; + } + +#if (SIM_DEBUG == 1) + printf(" Waiting for read (%02X)...\n", ch); #endif /* Increment character count - this is what calypso_sim_receive() hands back */ sim_rx_character_count++; /* Read byte from rx-fifo and write it to the issued buffer */ - *rx_buffer = (uint8_t) (readw(REG_SIM_DRX) & 0xFF); + *rx_buffer = ch; rx_buffer++; + + /* to maximise SIM access speed, stop waiting after + all the expected characters have been received. */ + if(sim_rx_max_character_count && sim_rx_character_count >= sim_rx_max_character_count) { +#if (SIM_DEBUG == 1) + puts(" Max characters received!\n"); +#endif + rxDoneFlag = 1; + } } } @@ -518,7 +565,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * uint8_t *status, /* Status word (2 byte array, see note 1) */ uint8_t mode) /* Mode of operation: 1=GET, 0=PUT */ - /* Note 1: You can use a null-pointer (0) if you are not interested in + /* Note 1: You can use a null-pointer (0) if you are not interested in the status word */ { uint8_t transmissionBuffer[256]; @@ -543,8 +590,8 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * #if (SIM_DEBUG == 1) puts("SIM-T0: Case 1: No input, No Output (See also GSM 11.11 Page 34)\n"); #endif - numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); - + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer, 2); + if(numberOfReceivedBytes == 2) { #if (SIM_DEBUG == 1) @@ -555,7 +602,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { status[0] = transmissionBuffer[0]; status[1] = transmissionBuffer[1]; - } + } return 0; } @@ -575,7 +622,8 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * puts("SIM-T0: Case 2: No input / Output of known length (See also GSM 11.11 Page 34)\n"); #endif - numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + /* TODO: waiting for one character is only OK if no error occurs */ + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer, 1); /* Error situation: The card has aborted, sends no data but a status word */ if(numberOfReceivedBytes == 2) @@ -588,11 +636,11 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { status[0] = transmissionBuffer[0]; status[1] = transmissionBuffer[1]; - } + } return 0; } - /* Acknoledge byte received */ + /* Acknowledge byte received */ else if(numberOfReceivedBytes == 1) { #if (SIM_DEBUG == 1) @@ -603,15 +651,21 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { #if (SIM_DEBUG == 1) puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); -#endif +#endif return -1; } /* Transmit body */ calypso_sim_transmit(data,p3le); - + + /* Ignore waiting char for RUN GSM ALGORITHM */ + /* TODO: implement proper handling of the "Procedure Bytes" + than this is no longer needed */ + if(ins == 0x88) + sim_ignore_waiting_char = 1; /* Receive status word */ - numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer, 2); + sim_ignore_waiting_char = 0; /* Check status word */ if(numberOfReceivedBytes == 2) @@ -633,7 +687,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { #if (SIM_DEBUG == 1) puts("SIM-T0: T0 Protocol error: Missing or invalid status word -- aborting!\n"); -#endif +#endif return -1; } } @@ -653,7 +707,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * puts("SIM-T0: Case 4: Input / No output (See also GSM 11.11 Page 34)\n"); #endif - numberOfReceivedBytes = calypso_sim_receive(data); + numberOfReceivedBytes = calypso_sim_receive(data, p3le + 1 + 2); /* Error situation: The card has aborted, sends no data but a status word */ if(numberOfReceivedBytes == 2) @@ -667,7 +721,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { status[0] = data[0]; status[1] = data[1]; - } + } return 0; } @@ -683,7 +737,7 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { #if (SIM_DEBUG == 1) puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); -#endif +#endif return -1; } @@ -695,8 +749,8 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * { status[0] = data[p3le + 1]; status[1] = data[p3le + 2]; - } - + } + /* Move data one position left to cut away the ACK-Byte */ memcpy(data,data+1,p3le); @@ -727,14 +781,20 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * /* Initialize simcard interface */ -void calypso_sim_init(void) +void calypso_sim_init(void (cb)(void)) { /* Register IRQ handler and turn interrupts on */ #if (SIM_DEBUG == 1) puts("SIM: Registering interrupt handler for simcard-interface\n"); #endif irq_register_handler(IRQ_SIMCARD, &sim_irq_handler); + #if 1 irq_config(IRQ_SIMCARD, 0, 0, 0xff); + #else + irq_config(IRQ_SIMCARD, 0, 0, 1); + #endif irq_enable(IRQ_SIMCARD); + + sim_wait_cb = cb; } diff --git a/src/target/firmware/include/calypso/sim.h b/src/target/firmware/include/calypso/sim.h index b2a2164..f0784e1 100755 --- a/src/target/firmware/include/calypso/sim.h +++ b/src/target/firmware/include/calypso/sim.h @@ -178,10 +178,10 @@ int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 * the status word */ /* Transmission of raw data */ -int calypso_sim_receive(uint8_t *data); /* Receive raw data through the sim interface */ +int calypso_sim_receive(uint8_t *data, uint8_t len); /* Receive raw data through the sim interface */ int calypso_sim_transmit(uint8_t *data, int length); /* Transmit raw data through the sim interface */ -void calypso_sim_init(void); /* Initialize simcard interface */ +void calypso_sim_init(void (cb)(void)); /* Initialize simcard interface */ /* Known Bugs: diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index aef12a5..de33c0a 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -462,6 +462,28 @@ static void l1ctl_rx_tch_mode_req(struct msgb *msg) l1ctl_tx_tch_mode_conf(tch_mode); } +#include + +void sim_apdu(uint16_t len, uint8_t *data); + +static void l1ctl_sim_req(struct msgb *msg) +{ + uint16_t len = msg->len - sizeof(struct l1ctl_hdr); + uint8_t *data = msg->data + sizeof(struct l1ctl_hdr); + +#if 1 /* for debugging only */ + { + int i; + printf("SIM Request (%u): ", len); + for (i = 0; i < len; i++) + printf("%02x ", data[i]); + puts("\n"); + } +#endif + + sim_apdu(len, data); +} + /* callback from SERCOMM when L2 sends a message to L1 */ static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) { @@ -522,6 +544,9 @@ static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) case L1CTL_TCH_MODE_REQ: l1ctl_rx_tch_mode_req(msg); break; + case L1CTL_SIM_REQ: + l1ctl_sim_req(msg); + break; } exit_msgbfree: -- 2.20.1