From d88025e6d95f262aeccf2601ef27f17691866eb0 Mon Sep 17 00:00:00 2001 From: Luca Melette Date: Wed, 25 Dec 2013 17:28:18 +0100 Subject: Initial release of libosmosim, firmware and host code to support gsmmap.org tools --- include/l1ctl_proto.h | 4 + src/host/layer23/configure.ac | 9 +- src/host/layer23/include/osmocom/bb/common/l1ctl.h | 3 + src/host/layer23/src/Makefile.am | 2 +- src/host/layer23/src/common/l1ctl.c | 57 +++- src/host/layer23/src/libosmosim/Makefile.am | 7 + src/host/layer23/src/libosmosim/libosmosim.c | 296 ++++++++++++++++++ src/host/layer23/src/libosmosim/libosmosim.h | 9 + src/target/firmware/apps/layer1/main.c | 4 +- src/target/firmware/calypso/sim.c | 334 +++++++++++---------- src/target/firmware/layer1/l23_api.c | 61 +++- 11 files changed, 622 insertions(+), 164 deletions(-) create mode 100644 src/host/layer23/src/libosmosim/Makefile.am create mode 100644 src/host/layer23/src/libosmosim/libosmosim.c create mode 100644 src/host/layer23/src/libosmosim/libosmosim.h diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index 771bf1c3..beceddf5 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -49,6 +49,10 @@ enum { L1CTL_CRYPTO_REQ, L1CTL_SIM_REQ, L1CTL_SIM_CONF, + L1CTL_SIM_POWERUP, + L1CTL_SIM_POWERDOWN, + L1CTL_SIM_RESET, + L1CTL_SIM_ATR, L1CTL_TCH_MODE_REQ, L1CTL_TCH_MODE_CONF, L1CTL_NEIGH_PM_REQ, diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac index 9335e66e..94fd79b0 100644 --- a/src/host/layer23/configure.ac +++ b/src/host/layer23/configure.ac @@ -10,7 +10,9 @@ dnl checks for programs AC_PROG_MAKE_SET AC_PROG_CC AC_PROG_INSTALL -AC_PROG_RANLIB +dnl AC_PROG_RANLIB +LT_INIT +AC_PROG_LIBTOOL dnl checks for libraries PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) @@ -30,8 +32,9 @@ dnl Checks for typedefs, structures and compiler characteristics AC_OUTPUT( src/Makefile src/common/Makefile - src/misc/Makefile - src/mobile/Makefile + dnl src/misc/Makefile + dnl src/mobile/Makefile + src/libosmosim/Makefile include/Makefile include/osmocom/Makefile include/osmocom/bb/Makefile diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h index 3534589d..1f56d7c7 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -62,6 +62,9 @@ int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, uint16_t arfcn_to); int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length); +int l1ctl_tx_sim_powerup(struct osmocom_ms *ms); +int l1ctl_tx_sim_powerdown(struct osmocom_ms *ms); +int l1ctl_tx_sim_reset(struct osmocom_ms *ms); /* Transmit L1CTL_VOICE_REQ */ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am index 58a5f7fb..a9174f18 100644 --- a/src/host/layer23/src/Makefile.am +++ b/src/host/layer23/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = common misc mobile +SUBDIRS = common libosmosim diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index 91bab897..f1bb2823 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -49,6 +49,8 @@ #include #include +extern void osmosim_sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg); +extern void osmosim_sim_up_resp(struct osmocom_ms *ms, struct msgb *msg); extern struct gsmtap_inst *gsmtap_inst; static int apdu_len = -1; @@ -650,6 +652,39 @@ int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length) return osmo_send_l1(ms, msg); } +int l1ctl_tx_sim_powerup(struct osmocom_ms *ms) +{ + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_SIM_POWERUP); + if (!msg) + return -1; + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_sim_powerdown(struct osmocom_ms *ms) +{ + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_SIM_POWERDOWN); + if (!msg) + return -1; + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_sim_reset(struct osmocom_ms *ms) +{ + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_SIM_RESET); + if (!msg) + return -1; + + return osmo_send_l1(ms, msg); +} + /* just forward the SIM response to the SIM handler */ static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg) { @@ -669,11 +704,28 @@ static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg) msgb_pull(msg, sizeof(struct l1ctl_hdr)); msg->l1h = NULL; - sim_apdu_resp(ms, msg); + osmosim_sim_apdu_resp(ms, msg); return 0; } +static int rx_l1_sim_atr(struct osmocom_ms *ms, struct msgb *msg) +{ + uint16_t len = msg->len - sizeof(struct l1ctl_hdr); + uint8_t *data = msg->data + sizeof(struct l1ctl_hdr); + + LOGP(DL1C, LOGL_INFO, "SIM ATR (len=%d) : %s\n", len, osmo_hexdump(data, len)); + + /* pull the L1 header from the msgb */ + msgb_pull(msg, sizeof(struct l1ctl_hdr)); + msg->l1h = NULL; + + osmosim_sim_up_resp(ms, msg); + + msgb_free(msg); + return 0; +} + /* Transmit L1CTL_PM_REQ */ int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, uint16_t arfcn_to) @@ -972,6 +1024,9 @@ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) case L1CTL_SIM_CONF: rc = rx_l1_sim_conf(ms, msg); break; + case L1CTL_SIM_ATR: + rc = rx_l1_sim_atr(ms, msg); + break; case L1CTL_NEIGH_PM_IND: rc = rx_l1_neigh_pm_ind(ms, msg); msgb_free(msg); diff --git a/src/host/layer23/src/libosmosim/Makefile.am b/src/host/layer23/src/libosmosim/Makefile.am new file mode 100644 index 00000000..bcc263a0 --- /dev/null +++ b/src/host/layer23/src/libosmosim/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I/home/gsmmap/jdk1.7.0_45/include -I/home/gsmmap/jdk1.7.0_45/include/linux +AM_CFLAGS = -Wall -g $(LIBOSMOCORE_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) + +lib_LTLIBRARIES = libosmosim.la +libosmosim_la_SOURCES = ../common/l1ctl.c ../common/logging.c ../common/l1l2_interface.c libosmosim.c +libosmosim_la_LIBADD = $(LDADD) diff --git a/src/host/layer23/src/libosmosim/libosmosim.c b/src/host/layer23/src/libosmosim/libosmosim.c new file mode 100644 index 00000000..e6f91646 --- /dev/null +++ b/src/host/layer23/src/libosmosim/libosmosim.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include + +#define LIBOSMOSIM_DEBUG 0 + +#define libosmosim_dbg(fmt, ...) \ + do { if (LIBOSMOSIM_DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) + +#define _GNU_SOURCE +#include + +#include "libosmosim.h" + +// layer 2 connection variables +static char *layer2_socket_path = "/tmp/osmocom_l2"; +static void *l23_ctx = NULL; +static struct osmocom_ms *ms = NULL; + +// locking variables +struct log_target *stderr_target; +static char lckfile[] = "/tmp/.osmosim_lock"; +static FILE *flckfile; + +// fake stuff that is needed in l1ctl.c and is never used in our code +struct gsmtap_inst *gsmtap_inst = NULL; +const uint16_t gsm610_bitorder[] = {}; + +// sim present in phone identifier +static int sim_present = 0; + +// global APDU buffer +static uint8_t apdu_resp[255]; +static size_t apdu_length = 0; + +/* + * helper locking functions to ensure only 1 operations is performed by a Osmocom BB phone at the same time + */ + +static int in_shutdown = 0; +int i = 0; +void unlock() { + int j = i++; + libosmosim_dbg("unlock() called, tring to unlock %d\n", j); + funlockfile(flckfile); + libosmosim_dbg("unlocked %d, unlock() exiting\n", j); +} + +void dolock() { + int j = i++; + libosmosim_dbg("dolock() called, tring to lock %d\n", j); + flockfile(flckfile); + libosmosim_dbg("locked %d, dolock() exiting\n", j); +} + +void lock() { + libosmosim_dbg("%s\n", "lock() called"); + if (in_shutdown) { + libosmosim_dbg("%s\n", "lock() waiting as in_shutdown is 1"); + sleep(1337); // just wait forever as we're shutdowning already, no action is allowed. + } + dolock(); + libosmosim_dbg("%s\n", "lock() exiting"); +} + +/* + * callback functions + */ + +/* this function is called-back when L1CTL_SIM_ATR message is received (when SIM needs to return ATR) */ +int osmosim_sim_up_resp(struct osmocom_ms *ms, struct msgb *msg) { + libosmosim_dbg("%s\n", "sim_up_resp() called"); + uint16_t len = msg->len - sizeof(struct l1ctl_hdr); + if (len > 0) { + sim_present = 1; + } + + uint8_t *data = msg->data; + int length = msg->len; + LOGP(DSIM, LOGL_INFO, "SIM ATR (len=%d) : %s\n", length, osmo_hexdump(data, length)); + apdu_length = length; + memcpy(apdu_resp, data, length); + unlock(); // unlock a lock performed by osmo_sim_powerup() and osmosim_reset() + libosmosim_dbg("%s\n", "sim_up_resp() exiting"); + return len; +} + +/* this function is called-back when L1CTL_SIM_CONF message is received (when SIM needs to return a response to APDU) */ +int osmosim_sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg) { + libosmosim_dbg("%s\n", "sim_apdu_resp() called"); + uint8_t *data = msg->data; + int length = msg->len; + uint8_t sw1, sw2; + /* process status */ + if (length < 2) { + msgb_free(msg); + return 0; + } + sw1 = data[length - 2]; + sw2 = data[length - 1]; + LOGP(DSIM, LOGL_INFO, "received APDU (len=%d sw1=0x%02x sw2=0x%02x)\n", length, sw1, sw2); + + apdu_length = length; + memcpy(apdu_resp, data, length); + + unlock(); + libosmosim_dbg("%s\n", "sim_apdu_resp() exiting"); + return 0; +} + +/* + * osmosim_* functions that communicate over layer 1 + */ + +/* initialize stderr_target and set default logging to all known debug symbols + LOGL_FATAL */ +void osmosim_log_init() { + libosmosim_dbg("%s\n", "osmosim_log_init() called"); + log_init(&log_info, NULL); + stderr_target = NULL; + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + const char *debug_default = "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DL1C"; + log_parse_category_mask(stderr_target, debug_default); + log_set_log_level(stderr_target, LOGL_FATAL); + libosmosim_dbg("%s\n", "osmosim_log_init() exiting"); +} + +/* open layer2 socket and prepare ms struct for communication with Osmocom BB phone */ +int osmosim_init() +{ + libosmosim_dbg("%s\n", "osmosim_init() called"); + int rc; + + flckfile = fopen(lckfile, "w"); // open a lock file + if (!stderr_target) { + osmosim_log_init(); + } + + l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + ms = talloc_zero(l23_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + return 0; + } + + sprintf(ms->name, "1"); + + rc = layer2_open(ms, layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return 0; + } + + libosmosim_dbg("%s\n", "osmosim_init() exiting"); + return 1; +} + +/* sets a loglevel if log_level != 0 as zero means "don't set loglevel only return the current one" */ +int osmosim_loglevel(int log_level) { + libosmosim_dbg("%s%s%s\n", "osmosim_log_level() called (log_level = ", log_level_str(log_level), ")"); + if (!stderr_target) { + + osmosim_log_init(); + } + + if (log_level) { + log_set_log_level(stderr_target, log_level); + } + + libosmosim_dbg("%s\n", "osmosim_log_level() exiting"); + return stderr_target->loglevel; +} + +/* power up the sim (actual voltage) */ +int osmosim_powerup() { + libosmosim_dbg("%s\n", "osmosim_powerup() called"); + in_shutdown = 0; + lock(); + l1ctl_tx_sim_powerup(ms); + osmo_select_main(0); // wait for write to the socket + osmo_select_main(0); // wait for read from the socket + + libosmosim_dbg("%s\n", "osmosim_powerup() exiting"); + return sim_present; // 1 if sim is in the phone, 0 if sim is absent +} + +/* power down the sim (actual voltage) */ +void osmosim_powerdown() { + libosmosim_dbg("%s\n", "osmosim_powerdown() called"); + in_shutdown = 1; + dolock(); + l1ctl_tx_sim_powerdown(ms); + osmo_select_main(0); // wait for write to the socket (no response required) + unlock(); + libosmosim_dbg("%s\n", "osmosim_powerdown() exiting"); +} + +/* trigger SIM reset (actuall reset pin on the sim) */ +int osmosim_reset() { + libosmosim_dbg("%s\n", "osmosim_reset() called"); + lock(); + l1ctl_tx_sim_reset(ms); + osmo_select_main(0); // wait for write to the socket + osmo_select_main(0); // wait for read from the socket + + libosmosim_dbg("%s\n", "osmosim_reset() exiting"); + return sim_present; +} + +/* transmit a APDU to the SIM and wait for response to arrive (sim_apdu_resp), then read it out of a global buffer) */ +int osmosim_transmit(char* data, unsigned int len, char** out) +{ + libosmosim_dbg("%s\n", "osmosim_transmit() called"); + lock(); + LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n", data[0], data[1]); + l1ctl_tx_sim_req(ms, (uint8_t*)data, len); + osmo_select_main(0); + osmo_select_main(0); + + if (out) { + *out = (char*)&apdu_resp; + } + libosmosim_dbg("%s\n", "osmosim_transmit() exiting"); + return apdu_length; +} + +/* perform lock without checking for shutdown as this is initiated during shutdown */ +void osmosim_exit() { + libosmosim_dbg("%s\n", "osmosim_exit() called"); + dolock(); + layer2_close(ms); + talloc_free(ms); + talloc_free(l23_ctx); + log_target_destroy(stderr_target); + stderr_target = NULL; + unlock(); + fclose(flckfile); + libosmosim_dbg("%s\n", "osmosim_exit() exiting"); +} + +/* + * Java highlevel API wrappers for functions above + */ + +JNIEXPORT jboolean JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_init (JNIEnv *env, jobject obj) { + return osmosim_init(); +} + +JNIEXPORT jint JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_loglevel (JNIEnv *env, jobject obj, jint log_level) { + return osmosim_loglevel(log_level); +} + +JNIEXPORT jbyteArray JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simPowerup (JNIEnv *env, jobject obj) { + + osmosim_powerup(); + + jbyteArray result; + result = (*env)->NewByteArray(env, apdu_length); + (*env)->SetByteArrayRegion(env, result, 0, apdu_length, (jbyte*)apdu_resp); + + return result; +} + +JNIEXPORT void JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simPowerdown (JNIEnv *env, jobject obj) { + osmosim_powerdown(); +} + +JNIEXPORT jboolean JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simReset (JNIEnv *env, jobject obj) { + return osmosim_reset(); +} + +JNIEXPORT jbyteArray JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_transmit (JNIEnv *env, jobject obj, jbyteArray data) { + + char req[255]; + int len; + char *resp; + + len = (*env)->GetArrayLength(env, data); + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)req); + + len = osmosim_transmit(req, len, &resp); + + jbyteArray result; + result = (*env)->NewByteArray(env, len); + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)resp); + + return result; +} + +JNIEXPORT void JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_exit (JNIEnv *env, jobject obj) { + osmosim_exit(); +} diff --git a/src/host/layer23/src/libosmosim/libosmosim.h b/src/host/layer23/src/libosmosim/libosmosim.h new file mode 100644 index 00000000..444e9f05 --- /dev/null +++ b/src/host/layer23/src/libosmosim/libosmosim.h @@ -0,0 +1,9 @@ +#include + +int osmosim_init(); +int osmosim_loglevel(int log_level); +int osmosim_powerup(); +void osmosim_powerdown(); +int osmosim_reset(); +int osmosim_transmit(char* data, unsigned int len, char** out); +void osmosim_exit(); diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c index 59ffe972..ab31b92c 100644 --- a/src/target/firmware/apps/layer1/main.c +++ b/src/target/firmware/apps/layer1/main.c @@ -102,9 +102,11 @@ int main(void) /* initialize SIM */ calypso_sim_init(); +#if 0 puts("Power up simcard:\n"); memset(atr,0,sizeof(atr)); atrLength = calypso_sim_powerup(atr); +#endif layer1_init(); @@ -167,7 +169,7 @@ static void key_handler(enum key_codes code, enum key_states state) } /* power down SIM, TODO: this will happen with every key pressed, put it somewhere else ! */ - calypso_sim_powerdown(); + //calypso_sim_powerdown(); } diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c index 752628fd..19501daa 100644 --- a/src/target/firmware/calypso/sim.c +++ b/src/target/firmware/calypso/sim.c @@ -43,6 +43,7 @@ /* Class that contains the following instructions */ #define SIM_GET_RESPONSE 0xC0 /* Get the response of a command from the card */ +#define SIM_FETCH 0x12 #define SIM_READ_BINARY 0xB0 /* Read file in binary mode */ #define SIM_READ_RECORD 0xB2 /* Read record in binary mode */ @@ -347,7 +348,7 @@ void calypso_sim_regdump(void) /* Receive raw data through the sim interface */ int calypso_sim_receive(uint8_t *data, uint8_t len) { - printd("Triggering SIM reception\n"); + printd("Triggering SIM reception, len = %d bytes\n", len); /* Prepare buffers and flags */ rx_buffer = data; @@ -396,12 +397,12 @@ void sim_irq_handler(enum irq_nr irq) { int regVal = readw(REG_SIM_IT); - /* Display interrupt information */ printd("SIM-ISR: "); if(regVal & REG_SIM_IT_SIM_NATR) { - printd(" No answer to reset!\n"); + puts("SIM: No answer to reset!\n"); + rxDoneFlag = 1; } /* Used by: calypso_sim_receive() to determine when the transmission @@ -410,7 +411,6 @@ void sim_irq_handler(enum irq_nr irq) if(regVal & REG_SIM_IT_SIM_WT) { printd(" Character underflow!\n"); rxDoneFlag = 1; - } if(regVal & REG_SIM_IT_SIM_OV) { @@ -444,31 +444,36 @@ void sim_irq_handler(enum irq_nr irq) /* 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) { - printd(" 0x60 received...\n"); - return; - } - - printd(" Waiting for read (%02X)...\n", ch); - - /* Increment character count - this is what - * calypso_sim_receive() hands back - */ - sim_rx_character_count++; + /* read while FIFO is not empty + otherwise we can get FIFO full if we wait for REG_SIM_IT_SIM_RX for each char */ + while ((readw(REG_SIM_STAT) & REG_SIM_STAT_STATFIFOEMPTY) == 0) { + uint8_t ch = (uint8_t) (readw(REG_SIM_DRX) & 0xFF); + + /* ignore NULL procedure byte */ + if(ch == 0x60 && sim_ignore_waiting_char) { + printd(" 0x60 received...\n"); + return; + } - /* Read byte from rx-fifo and write it to the issued buffer */ - *rx_buffer = ch; - rx_buffer++; + printd(" Waiting for read (%02X)...\n", ch); - /* 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) { - printd(" Max characters received!\n"); - rxDoneFlag = 1; + /* 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 = 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) { + printd(" Max characters received!\n"); + rxDoneFlag = 1; + break; + } } } } @@ -477,7 +482,7 @@ void sim_irq_handler(enum irq_nr irq) void sim_apdu(uint16_t len, uint8_t *data) { if (sim_state != SIM_STATE_IDLE) { - puts("Sim reader currently busy...\n"); + puts("SIM: reader currently busy...\n"); return; } memcpy(sim_data, data, len); @@ -494,139 +499,141 @@ void sim_handler(void) static uint16_t length; switch (sim_state) { - case SIM_STATE_IDLE: - if (!sim_len) - break; /* wait for SIM command */ - /* check if instructions expects a response */ - 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/RECORD needs SIM_APDU_GET */ - (sim_len >= 5 && sim_data[0] == SIM_CLASS && - (sim_data[1] == SIM_READ_BINARY || - sim_data[1] == SIM_READ_RECORD))) - mode = SIM_APDU_GET; - else - mode = SIM_APDU_PUT; - - length = sim_data[4]; - - /* allocate space for expected response */ - msg = msgb_alloc_headroom(256, L3_MSG_HEAD + case SIM_STATE_IDLE: + if (!sim_len) + break; /* wait for SIM command */ + /* check if instructions expects a response */ + if (/* GET RESPONSE needs SIM_APDU_GET */ + (sim_len == 5 && sim_data[0] == SIM_CLASS && + (sim_data[1] == SIM_GET_RESPONSE || sim_data[1] == SIM_FETCH) && sim_data[2] == 0x00 && + sim_data[3] == 0x00) || + /* READ BINARY/RECORD needs SIM_APDU_GET */ + (sim_len >= 5 && sim_data[0] == SIM_CLASS && + (sim_data[1] == SIM_READ_BINARY || + sim_data[1] == SIM_READ_RECORD))) + mode = SIM_APDU_GET; + else + mode = SIM_APDU_PUT; + + length = sim_data[4]; + + /* allocate space for expected response */ + msg = msgb_alloc_headroom(256, L3_MSG_HEAD + sizeof(struct l1ctl_hdr), "l1ctl1"); - response = msgb_put(msg, length + 2 + 1); - - sim_state = SIM_STATE_TX_HEADER; - - /* send APDU header */ - calypso_sim_transmit(sim_data, 5); - break; - case SIM_STATE_TX_HEADER: - if (!txDoneFlag) - break; /* wait until header is transmitted */ - /* Disable all interrupt driven functions */ - writew(0xFF, REG_SIM_MASKIT); - /* Case 1: No input, No Output */ - if (length == 0) { - sim_state = SIM_STATE_RX_STATUS; - calypso_sim_receive(response + 1, 2); + response = msgb_put(msg, length + 2 + 1); + + sim_state = SIM_STATE_TX_HEADER; + + /* send APDU header */ + calypso_sim_transmit(sim_data, 5); break; - } - /* Case 2: No input / Output of known length */ - if (mode == SIM_APDU_PUT) { - sim_state = SIM_STATE_RX_ACK; - calypso_sim_receive(response, 1); + case SIM_STATE_TX_HEADER: + if (!txDoneFlag) + break; /* wait until header is transmitted */ + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + /* Case 1: No input, No Output */ + if (length == 0) { + sim_state = SIM_STATE_RX_STATUS; + calypso_sim_receive(response + 1, 2); + break; + } + /* Case 2: No input / Output of known length */ + if (mode == SIM_APDU_PUT) { + sim_state = SIM_STATE_RX_ACK; + calypso_sim_receive(response, 1); + break; + /* Case 4: Input / No output */ + } else { + sim_state = SIM_STATE_RX_ACK_DATA; + calypso_sim_receive(response, length + 1 + 2); + } break; - /* Case 4: Input / No output */ - } else { - sim_state = SIM_STATE_RX_ACK_DATA; - calypso_sim_receive(response, length + 1 + 2); - } - break; - case SIM_STATE_RX_STATUS: - if (!rxDoneFlag) - break; /* wait until data is received */ - /* Disable all interrupt driven functions */ - writew(0xFF, REG_SIM_MASKIT); - /* disable special ignore case */ - sim_ignore_waiting_char = 0; - /* wrong number of bytes received */ - if (sim_rx_character_count != 2) { - puts("SIM: Failed to read status\n"); - goto error; - } - msgb_pull(msg, length + 1); /* pull up to status info */ - goto queue; - case SIM_STATE_RX_ACK: - if (!rxDoneFlag) - break; /* wait until data is received */ - /* Disable all interrupt driven functions */ - writew(0xFF, REG_SIM_MASKIT); - /* error received */ - if (sim_rx_character_count == 2) { - puts("SIM: command failed\n"); - msgb_pull(msg, msg->len - 2); - msg->data[0] = response[0]; - msg->data[1] = response[1]; + case SIM_STATE_RX_STATUS: + if (!rxDoneFlag) + break; /* wait until data is received */ + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + /* disable special ignore case */ + sim_ignore_waiting_char = 0; + /* wrong number of bytes received */ + if (sim_rx_character_count != 2) { + puts("SIM: Failed to read status\n"); + goto error; + } + msgb_pull(msg, length + 1); /* pull up to status info */ goto queue; - } - /* wrong number of bytes received */ - if (sim_rx_character_count != 1) { - puts("SIM: ACK read failed\n"); - goto error; - } - if (response[0] != sim_data[1]) { - puts("SIM: ACK does not match request\n"); - goto error; - } - sim_state = SIM_STATE_TX_DATA; - calypso_sim_transmit(sim_data + 5, length); - break; - case SIM_STATE_TX_DATA: - if (!txDoneFlag) - break; /* wait until data is transmitted */ - /* Disable all interrupt driven functions */ - writew(0xFF, REG_SIM_MASKIT); - /* Ignore waiting char for RUN GSM ALGORITHM */ - /* TODO: implement proper handling of the "Procedure Bytes" - than this is no longer needed */ - if(sim_data[1] == 0x88) - sim_ignore_waiting_char = 1; - sim_state = SIM_STATE_RX_STATUS; - calypso_sim_receive(response + length + 1, 2); - break; - case SIM_STATE_RX_ACK_DATA: - if (!rxDoneFlag) - break; /* wait until data is received */ - /* Disable all interrupt driven functions */ - writew(0xFF, REG_SIM_MASKIT); - /* error received */ - if (sim_rx_character_count == 2) { - puts("SIM: command failed\n"); - msgb_pull(msg, msg->len - 2); - msg->data[0] = response[0]; - msg->data[1] = response[1]; + case SIM_STATE_RX_ACK: + if (!rxDoneFlag) + break; /* wait until data is received */ + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + /* error received */ + if (sim_rx_character_count == 2) { + puts("SIM: command failed\n"); + msgb_pull(msg, msg->len - 2); + msg->data[0] = response[0]; + msg->data[1] = response[1]; + goto queue; + } + /* wrong number of bytes received */ + if (sim_rx_character_count != 1) { + puts("SIM: ACK read failed\n"); + goto error; + } + if (response[0] != sim_data[1]) { + puts("SIM: ACK does not match request\n"); + goto error; + } + sim_state = SIM_STATE_TX_DATA; + calypso_sim_transmit(sim_data + 5, length); + break; + case SIM_STATE_TX_DATA: + if (!txDoneFlag) + break; /* wait until data is transmitted */ + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + /* Ignore waiting char for RUN GSM ALGORITHM */ + /* TODO: implement proper handling of the "Procedure Bytes" + than this is no longer needed */ + if(sim_data[1] == 0x88) + sim_ignore_waiting_char = 1; + sim_state = SIM_STATE_RX_STATUS; + calypso_sim_receive(response + length + 1, 2); + break; + case SIM_STATE_RX_ACK_DATA: + if (!rxDoneFlag) + break; /* wait until data is received */ + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + /* error received */ + if (sim_rx_character_count == 2) { + puts("SIM: command failed\n"); + msgb_pull(msg, msg->len - 2); + msg->data[0] = response[0]; + msg->data[1] = response[1]; + goto queue; + } + /* wrong number of bytes received */ + if (sim_rx_character_count != length + 1 + 2) { + puts("SIM: Failed to read data\n"); + goto error; + } + msgb_pull(msg, 1); /* pull ACK byte */ goto queue; - } - /* wrong number of bytes received */ - if (sim_rx_character_count != length + 1 + 2) { - puts("SIM: Failed to read data\n"); - goto error; - } - msgb_pull(msg, 1); /* pull ACK byte */ - goto queue; } return; error: msgb_pull(msg, msg->len - 2); - msg->data[0] = 0; - msg->data[1] = 0; + msg->data[0] = 0xba; + msg->data[1] = 0xad; queue: +#ifdef DEBUG /* for debugging only */ printf("SIM Response (%d): %s\n", msg->len, - osmo_hexdump(msg->data, msg->len)); + osmo_hexdump(msg->data, msg->len)); +#endif l1h = (struct l1ctl_hdr *) msgb_push(msg, sizeof(*l1h)); l1h->msg_type = L1CTL_SIM_CONF; l1h->flags = 0; @@ -659,10 +666,14 @@ void calypso_sim_init(void) /* Apply power to the simcard (use nullpointer to ignore atr) */ int calypso_sim_powerup(uint8_t *atr) { + /* put state machine back into idle */ + sim_len = 0; + sim_state = SIM_STATE_IDLE; + /* Enable level shifters and voltage regulator */ #if 1 // 2.9V twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN - | VRPCSIM_SIMSEL); + | VRPCSIM_SIMSEL); #else // 1.8V twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN); #endif @@ -676,8 +687,8 @@ int calypso_sim_powerup(uint8_t *atr) /* Release reset */ writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFBYPASS - | REG_SIM_CONF1_CONFSRSTLEV - | REG_SIM_CONF1_CONFSVCCLEV, REG_SIM_CONF1); + | REG_SIM_CONF1_CONFSRSTLEV + | REG_SIM_CONF1_CONFSVCCLEV, REG_SIM_CONF1); printd(" * Reset released!\n"); /* Catch ATR */ @@ -687,14 +698,19 @@ int calypso_sim_powerup(uint8_t *atr) ; } - return 0; + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + + return sim_rx_character_count; } /* Powerdown simcard */ void calypso_sim_powerdown(void) { - writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFBYPASS, REG_SIM_CONF1); + // according to the specification CONFTXRX | CONFSCLKEN is a default reset value of conf1 register + // if CONFSVCCLEV or CONFSRSTLEV remains enabled even without CONFBYPASS, SIM is never properly powered down + writew(REG_SIM_CONF1_CONFTXRX | REG_SIM_CONF1_CONFSCLKEN, REG_SIM_CONF1); printd(" * Reset pulled down!\n"); delay_ms(SIM_OPERATION_DELAY); @@ -710,14 +726,13 @@ void calypso_sim_powerdown(void) twl3025_reg_write(VRPCSIM, 0); printd(" * Power disabled!\n"); delay_ms(SIM_OPERATION_DELAY); - + return; } /* reset the simcard (see note 1) */ int calypso_sim_reset(uint8_t *atr) { - /* Pull reset down */ writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFSRSTLEV, REG_SIM_CONF1); @@ -725,6 +740,10 @@ int calypso_sim_reset(uint8_t *atr) delay_ms(SIM_OPERATION_DELAY); + /* put state machine back into idle */ + sim_len = 0; + sim_state = SIM_STATE_IDLE; + /* Pull reset down */ writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFSRSTLEV, REG_SIM_CONF1); printd(" * Reset released!\n"); @@ -736,6 +755,9 @@ int calypso_sim_reset(uint8_t *atr) ; } - return 0; + /* Disable all interrupt driven functions */ + writew(0xFF, REG_SIM_MASKIT); + + return sim_rx_character_count; } diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index ae39e634..4bf8a83a 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -20,7 +20,8 @@ * */ -#define DEBUG +/* Uncomment to debug */ +/* #define DEBUG */ #include #include @@ -571,7 +572,7 @@ 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 */ +#ifdef DEBUG /* for debugging only */ { int i; printf("SIM Request (%u): ", len); @@ -584,6 +585,53 @@ static void l1ctl_sim_req(struct msgb *msg) sim_apdu(len, data); } +void l1ctl_tx_sim_up(uint8_t* atr, uint8_t atrLength) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_SIM_ATR); + uint8_t *data; + data = (uint8_t*) msgb_put(msg, atrLength); + memcpy(data, atr, atrLength); + msg->len += atrLength; + + l1_queue_for_l2(msg); +} + +static int powered_up = 0; + +static void l1ctl_rx_sim_powerup() +{ + uint8_t atr[20]; + uint8_t atrLength = 0; + + if (powered_up) { + puts("SIM: sim is already powered up, calling power down first!\n"); + calypso_sim_powerdown(); + } + + memset(atr,0,sizeof(atr)); + + atrLength = calypso_sim_powerup(atr); + l1ctl_tx_sim_up(atr, atrLength); + powered_up = 1; +} + +static void l1ctl_rx_sim_powerdown() +{ + calypso_sim_powerdown(); + powered_up = 0; +} + +static void l1ctl_rx_sim_reset() +{ + uint8_t atr[20]; + uint8_t atrLength = 0; + + memset(atr,0,sizeof(atr)); + + atrLength = calypso_sim_reset(atr); + l1ctl_tx_sim_up(atr, atrLength); +} + static struct llist_head l23_rx_queue = LLIST_HEAD_INIT(l23_rx_queue); /* callback from SERCOMM when L2 sends a message to L1 */ @@ -675,6 +723,15 @@ void l1a_l23_handler(void) case L1CTL_SIM_REQ: l1ctl_sim_req(msg); break; + case L1CTL_SIM_POWERUP: + l1ctl_rx_sim_powerup(); + break; + case L1CTL_SIM_POWERDOWN: + l1ctl_rx_sim_powerdown(); + break; + case L1CTL_SIM_RESET: + l1ctl_rx_sim_reset(); + break; } exit_msgbfree: -- cgit v1.2.3