SAP client - revisited

Submitted by Tomcsányi, Domonkos on April 28, 2015, 1:39 p.m.

Details

Message ID 2083838205.22261.1430228384417.JavaMail.zimbra@tomcsanyi.net
State New
Headers show

Commit Message

Tomcsányi, Domonkos April 28, 2015, 1:39 p.m.
Hi,

On Friday, April 24, 2015 4:41:53 PM, Nico Golde wrote:

>> One thing however that I think is strange, and worth mentioning: I'm not
>> sure why Nico decided to implement the switch between phone and SAP-client
>> inside of l1ctl.c, for me it would feel better to do it in sim.c (since
>> sim.c deals with SIM activities, l1ctl should deal only with L1 stuff...also
>> the current SAP client calls back to sim.c, but receives data from l1ctl -
>> little bit confusing), but I left it as is because of not knowing exactly
>> the thoughts behind it.
> 
> I don't remember details to be honest as this is too long ago, but I think
> there was no real design decision behind this other than that the code that is
> sending traffic to the SIM was already in l1ctl and I just added the switch...

All right, thanks for clarifying. I went ahead and did the change: now sim.c calls either l1ctl or sap_interface and they both call back to sim.c when they have a result. The function that gets called in sap_interface.c has now the same signature as the one in l1ctl suggesting they provide an interface for the same task (which they do indeed).

I attached the revised patch.

Some background info/documentation:
By default the mobile app will try to use the SAP backend instead of the built in SIM reader. If it fails to connect to the default SAP socket (/tmp/osmocom_sap) it'll fallback to the phone's built-in SIM slot.
To use the SAP backend you'll need softSIM which will create the socket and also interact with a PC/SC reader or a previously captured file (sim_os). First you need to start up the demo_server with the right parameters based on your setup (-s unix to create the socket for the mobile app, -t type depends on whether you have a PC/SC reader or not). After that osmocon could be started, then the mobile app, then the phone itself.
When starting up the mobile app one can observe the debug information from the SAP interface by default.

Regards,
Domi

Comments

Sylvain Munaut April 28, 2015, 1:57 p.m.
Patch looked good. Pushed.

Patch hide | download patch | download mbox

diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
index ab7c250..593b884 100644
--- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
+++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
@@ -24,6 +24,8 @@  struct osmocom_ms;
 
 struct osmosap_entity {
 	osmosap_cb_t msg_handler;
+        uint8_t sap_state;
+        uint16_t max_msg_size;
 };
 
 struct osmol1_entity {
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_interface.h b/src/host/layer23/include/osmocom/bb/common/sap_interface.h
index f2f577a..bf19356 100644
--- a/src/host/layer23/include/osmocom/bb/common/sap_interface.h
+++ b/src/host/layer23/include/osmocom/bb/common/sap_interface.h
@@ -5,7 +5,70 @@  typedef int (*osmosap_cb_t)(struct msgb *msg, struct osmocom_ms *ms);
 
 int sap_open(struct osmocom_ms *ms, const char *socket_path);
 int sap_close(struct osmocom_ms *ms);
-int osmosap_send(struct osmocom_ms *ms, struct msgb *msg);
+int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length);
 int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb);
+int osmosap_sapsocket(struct osmocom_ms *ms, const char *path);
+int osmosap_init(struct osmocom_ms *ms);
+
+enum osmosap_state {
+	SAP_NOT_CONNECTED,
+	SAP_IDLE,
+	SAP_CONNECTION_UNDER_NEGOTIATION,
+	SAP_PROCESSING_ATR_REQUEST,
+	SAP_PROCESSING_APDU_REQUEST
+};
+
+/* BTSAP 1.13 */
+enum osmosap_msg_type {
+	SAP_CONNECT_REQ = 0x00,
+	SAP_CONNECT_RESP = 0x01,
+	SAP_DISCONNECT_REQ = 0x02,
+	SAP_DISCONNECT_RESP = 0x03,
+	SAP_DISCONNECT_IND = 0x04,
+	SAP_TRANSFER_APDU_REQ = 0x05,
+	SAP_TRANSFER_APDU_RESP = 0x06,
+	SAP_TRANSFER_ATR_REQ = 0x07,
+	SAP_TRANSFER_ATR_RESP = 0x08,
+	SAP_POWER_SIM_OFF_REQ = 0x09,
+	SAP_POWER_SIM_OFF_RESP = 0x0A,
+	SAP_POWER_SIM_ON_REQ = 0x0B,
+	SAP_POWER_SIM_ON_RESP = 0x0C,
+	SAP_RESET_SIM_REQ = 0x0D,
+	SAP_RESET_SIM_RESP = 0x0E,
+	SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F,
+	SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10,
+	SAP_STATUS_IND = 0x11,
+	SAP_ERROR_RESP = 0x12,
+	SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13,
+	SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14
+};
+
+/* BTSAP 5.2 */
+enum osmosap_param_type {
+	SAP_MAX_MSG_SIZE = 0x00,
+	SAP_CONNECTION_STATUS = 0x01,
+	SAP_RESULT_CODE = 0x02,
+	SAP_DISCONNECTION_TYPE = 0x03,
+	SAP_COMMAND_APDU = 0x04,
+	SAP_COMMAND_APDU_7816 = 0x10,
+	SAP_RESPONSE_APDU = 0x05,
+	SAP_ATR = 0x06,
+	SAP_CARD_READER_STATUS = 0x07,
+	SAP_STATUS_CHANGE = 0x08,
+	SAP_TRANSPORT_PROTOCOL = 0x09
+};
+
+struct sap_param {
+	uint8_t id;
+	uint16_t len;
+	uint8_t *value;
+};
+
+struct sap_msg {
+	uint8_t id;
+	uint8_t num_params;
+	struct sap_param *params;
+};
+
 
 #endif /* _SAP_INTERFACE_H */
diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c
index c75872e..dfb3079 100644
--- a/src/host/layer23/src/common/l1ctl.c
+++ b/src/host/layer23/src/common/l1ctl.c
@@ -644,11 +644,10 @@  int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
 	msg = osmo_l1_alloc(L1CTL_SIM_REQ);
 	if (!msg)
 		return -1;
-
 	dat = msgb_put(msg, length);
 	memcpy(dat, data, length);
 
-	return osmo_send_l1(ms, msg);
+        return osmo_send_l1(ms, msg);
 }
 
 /* just forward the SIM response to the SIM handler */
diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c
index 1dad748..ce20ebc 100644
--- a/src/host/layer23/src/common/sap_interface.c
+++ b/src/host/layer23/src/common/sap_interface.c
@@ -3,6 +3,7 @@ 
 /* (C) 2010 by Holger Hans Peter Freyther
  * (C) 2010 by Harald Welte <laforge@gnumonks.org>
  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2011 by Nico Golde <nico@ngolde.de>
  *
  * All Rights Reserved
  *
@@ -27,6 +28,7 @@ 
 #include <osmocom/bb/common/sap_interface.h>
 
 #include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -42,72 +44,463 @@ 
 #define GSM_SAP_LENGTH 300
 #define GSM_SAP_HEADROOM 32
 
-static int sap_read(struct osmo_fd *fd)
+static void sap_connect(struct osmocom_ms *ms);
+
+static const struct value_string sap_param_names[] = {
+	{SAP_MAX_MSG_SIZE,			"MaxMsgSize"},
+	{SAP_CONNECTION_STATUS, 	"ConnectionStatus"},
+	{SAP_RESULT_CODE,			"ResultCode"},
+	{SAP_DISCONNECTION_TYPE,	"DisconnectionType"},
+	{SAP_COMMAND_APDU,			"CommandAPDU"},
+	{SAP_COMMAND_APDU_7816,		"CommandAPDU7816"},
+	{SAP_RESPONSE_APDU,			"ResponseAPDU"},
+	{SAP_ATR,					"ATR"},
+	{SAP_CARD_READER_STATUS,	"CardReaderStatus"},
+	{SAP_STATUS_CHANGE,			"StatusChange"},
+	{SAP_TRANSPORT_PROTOCOL,	"TransportProtocol"}
+};
+
+static const struct value_string sap_msg_names[] = {
+	{SAP_CONNECT_REQ,						"CONNECT_REQ"},
+	{SAP_CONNECT_RESP,						"CONNECT_RESP"},
+	{SAP_DISCONNECT_REQ,					"DISCONNECT_REQ"},
+	{SAP_DISCONNECT_RESP,					"DISCONNECT_RESP"},
+	{SAP_DISCONNECT_IND,					"DISCONNECT_IND"},
+	{SAP_TRANSFER_APDU_REQ,					"TRANSFER_APDU_REQ"},
+	{SAP_TRANSFER_APDU_RESP,				"TRANSFER_APDU_RESP"},
+	{SAP_TRANSFER_ATR_REQ,					"TRANSFER_ATR_REQ"},
+	{SAP_TRANSFER_ATR_RESP,					"TRANSFER_ATR_RESP"},
+	{SAP_POWER_SIM_OFF_REQ,					"POWER_SIM_OFF_REQ"},
+	{SAP_POWER_SIM_OFF_RESP,				"POWER_SIM_OFF_RESP"},
+	{SAP_POWER_SIM_ON_REQ,					"POWER_SIM_ON_REQ"},
+	{SAP_POWER_SIM_ON_RESP,					"POWER_SIM_ON_RESP"},
+	{SAP_RESET_SIM_REQ,						"RESET_SIM_REQ"},
+	{SAP_RESET_SIM_RESP,					"RESET_SIM_RESP"},
+	{SAP_TRANSFER_CARD_READER_STATUS_REQ,	"TRANSFER_CARD_READER_STATUS_REQ"},
+	{SAP_TRANSFER_CARD_READER_STATUS_RESP,	"TRANSFER_CARD_READER_STATUS_RESP"},
+	{SAP_STATUS_IND,						"STATUS_IND"},
+	{SAP_ERROR_RESP,						"ERROR_RESP"},
+	{SAP_SET_TRANSPORT_PROTOCOL_REQ,		"SET_TRANSPORT_PROTOCOL_REQ"},
+	{SAP_SET_TRANSPORT_PROTOCOL_RESP,		"SET_TRANSPORT_PROTOCOL_RESP"}
+};
+
+/* BTSAP table 5.18 */
+static const struct value_string sap_result_names[] = {
+	{0, "OK, request processed correctly"},
+	{1, "Error, no reason defined"},
+	{2, "Error, card not accessible"},
+	{3, "Error, card (already) powered off"},
+	{4, "Error, card removed"},
+	{5, "Error, card already powered on"},
+	{6, "Error, data not available"},
+	{7, "Error, not supported"}
+};
+
+static const struct value_string sap_status_change_names[] = {
+	{0, "Unknown Error"},
+	{1, "Card reset"},
+	{2, "Card not accessible"},
+	{3, "Card removed"},
+	{4, "Card inserted"},
+	{5, "Card recovered"},
+};
+
+static const struct value_string sap_status_names[] = {
+	{0, "OK, Server can fulfill requirements"},
+	{1, "Error, Server unable to establish connection"},
+	{2, "Error, Server does not support maximum message size"},
+	{3, "Error, maximum message size by Client is too small"},
+	{4, "OK, ongoing call"}
+};
+
+static struct msgb *sap_create_msg(uint8_t id, uint8_t num_params, struct sap_param *params)
 {
 	struct msgb *msg;
-	uint16_t len;
-	int rc;
-	struct osmocom_ms *ms = (struct osmocom_ms *) fd->data;
+	uint8_t *msgp;
+	uint8_t i, plen, padding = 0;
 
-	msg = msgb_alloc_headroom(GSM_SAP_LENGTH+GSM_SAP_HEADROOM, GSM_SAP_HEADROOM, "Layer2");
+	msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap");
 	if (!msg) {
 		LOGP(DSAP, LOGL_ERROR, "Failed to allocate msg.\n");
+		return NULL;
+	}
+
+	/* BTSAP 5.1 */
+	msgb_put_u8(msg, id);
+	msgb_put_u8(msg, num_params);
+	msgb_put_u16(msg, 0);
+
+	for(i=0; i<num_params; i++){
+		plen = params[i].len;
+		msgb_put_u8(msg, params[i].id);
+		msgb_put_u8(msg, 0);
+		msgb_put_u16(msg, plen);
+		if(plen % 4){
+			padding = 4 - (plen % 4);
+		}
+		msgp = msgb_put(msg, plen + padding);
+		memcpy(msgp, params[i].value, plen);
+
+		if(padding){
+			memset(msgp + plen, 0, padding);
+		}
+	}
+
+	return msg;
+}
+
+static int osmosap_send(struct osmocom_ms *ms, struct msgb *msg)
+{
+	if(ms->sap_entity.sap_state == SAP_NOT_CONNECTED && !ms->sap_entity.sap_state == SAP_CONNECTION_UNDER_NEGOTIATION)
+		sap_connect(ms);
+
+	if (ms->sap_wq.bfd.fd <= 0)
+		return -EINVAL;
+
+	if (osmo_wqueue_enqueue(&ms->sap_wq, msg) != 0) {
+		LOGP(DSAP, LOGL_ERROR, "Failed to enqueue msg.\n");
+		msgb_free(msg);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sap_parse_result(struct sap_param *param)
+{
+	if(param->id != SAP_RESULT_CODE){
+		LOGP(DSAP, LOGL_INFO, "> Parameter id: %u no valid result type\n", param->id);
+		return -1;
+	} else {
+		LOGP(DSAP, LOGL_INFO, "> RESULT CODE: %s\n",
+				get_value_string(sap_result_names, param->value[0]));
+	}
+
+	if(param->value[0] > sizeof(sap_result_names)/sizeof(struct value_string)){
+		return -1;
+	}
+
+	return 0;
+}
+
+static uint8_t *sap_get_param(uint8_t *data, struct sap_param *param)
+{
+	uint8_t *dptr = data;
+	uint8_t padlen;
+
+	param->id = *dptr++;
+	/* skip reserved byte */
+	dptr++;
+	param->len = *dptr << 8;
+	dptr++;
+	param->len |= *dptr++;
+	param->value = talloc_zero_size(NULL, param->len);
+	memcpy(param->value, dptr, param->len);
+
+	/* skip parameter and padding and return pointer to next parameter */
+	dptr += param->len;
+	if(param->len % 4){
+		padlen = (4 - param->len % 4);
+	} else {
+		padlen = 0;
+	}
+	dptr += padlen;
+
+	return dptr;
+}
+
+static void sap_msg_free(struct sap_msg *msg)
+{
+	uint8_t i;
+	for(i=0; i<msg->num_params; i++){
+		talloc_free(msg->params[i].value);
+		talloc_free(msg->params);
+	}
+	talloc_free(msg);
+}
+
+static struct sap_msg *sap_parse_msg(uint8_t *data)
+{
+	struct sap_msg *msg = talloc_zero(NULL, struct sap_msg);
+	uint8_t *ptr = data;
+	uint8_t i;
+
+	if(!msg){
+		return NULL;
+	}
+
+	msg->id = *ptr++;
+	LOGP(DSAP, LOGL_INFO, "> %s \n", get_value_string(sap_msg_names, msg->id));
+
+	msg->num_params = *ptr++;
+	/* skip two reserved null bytes, BTSAP 5.1 */
+	ptr += 2;
+        
+	msg->params = talloc_zero_size(NULL, sizeof(struct sap_param) * msg->num_params);
+
+	for(i=0; i<msg->num_params; i++){
+		ptr = sap_get_param(ptr, &msg->params[i]);
+		LOGP(DSAP, LOGL_INFO, "> %s %s\n",
+				get_value_string(sap_param_names, msg->params[i].id),
+				osmo_hexdump(msg->params[i].value, msg->params[i].len));
+	}
+
+	return msg;
+}
+
+static void sap_apdu_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
+{
+	struct msgb *msg;
+	uint8_t *apdu;
+	msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap");
+	if(!msg){
+		LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory.\n");
+		return;
+	}
+
+	apdu = msgb_put(msg, len);
+	memcpy(apdu, data, len);
+
+	LOGP(DSAP, LOGL_DEBUG, "Forwarding APDU to SIM handler.\n");
+	sim_apdu_resp(ms, msg);
+}
+
+static int sap_adapt_msg_size(struct osmocom_ms *ms, struct sap_param *param)
+{
+	uint16_t size;
+	size = (param->value[0] << 8) | param->value[1];
+	if(size != ms->sap_entity.max_msg_size && size > 0){
+		LOGP(DSAP, LOGL_NOTICE, "Server can not handle max_msg_size, adapting.\n");
+		ms->sap_entity.max_msg_size = size;
+		return -1;
+	}
+	return 0;
+}
+
+static void sap_atr(struct osmocom_ms *ms)
+{
+	struct msgb *msg;
+	if(ms->sap_entity.sap_state != SAP_IDLE){
+		LOGP(DSAP, LOGL_ERROR, "Attempting to send ATR request while not being idle.\n");
+		return;
+	}
+
+	msg = sap_create_msg(SAP_TRANSFER_ATR_REQ, 0, NULL);
+	if(!msg)
+		return;
+
+	osmosap_send(ms, msg);
+	ms->sap_entity.sap_state = SAP_PROCESSING_ATR_REQUEST;
+}
+
+static void sap_parse_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
+{
+	struct sap_msg *msg = NULL;
+	if(len > ms->sap_entity.max_msg_size){
+		LOGP(DSAP, LOGL_ERROR, "Read more data than allowed by max_msg_size, ignoring.\n");
+		return;
+	}
+
+	msg = sap_parse_msg(data);
+	if(!msg){
+		sap_msg_free(msg);
+		return;
+	}
+
+	switch(msg->id){
+	case SAP_CONNECT_RESP:
+		LOGP(DSAP, LOGL_INFO, "Status: %s\n", get_value_string(sap_status_names, msg->params[0].value[0]));
+		if(msg->params[0].value[0] == 0){
+			ms->sap_entity.sap_state = SAP_IDLE;
+		}
+		if(msg->num_params == 2 && msg->params[1].len == 2){
+			if(sap_adapt_msg_size(ms, &msg->params[1]) < 0) {
+				ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
+			} else {
+				sap_atr(ms);
+			}
+		}
+		break;
+	case SAP_DISCONNECT_RESP:
+		ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
+		break;
+	case SAP_STATUS_IND:
+		LOGP(DSAP, LOGL_INFO, "New card state: %s\n", get_value_string(sap_status_change_names,
+					msg->params[0].value[0]));
+		if(msg->params[0].value[0] != 1){
+			/* TODO: handle case in which the card is not ready yet */
+		}
+		break;
+	case SAP_TRANSFER_ATR_RESP:
+		if(ms->sap_entity.sap_state != SAP_PROCESSING_ATR_REQUEST){
+			LOGP(DSAP, LOGL_ERROR, "got ATR resp in state: %u\n", ms->sap_entity.sap_state);
+			return;
+		}
+		if(msg->num_params >= 2){
+			LOGP(DSAP, LOGL_INFO, "ATR: %s\n", osmo_hexdump(msg->params[1].value, msg->params[1].len));
+		}
+		ms->sap_entity.sap_state = SAP_IDLE;
+		break;
+	case SAP_TRANSFER_APDU_RESP:
+		if(ms->sap_entity.sap_state != SAP_PROCESSING_APDU_REQUEST){
+			LOGP(DSAP, LOGL_ERROR, "got APDU resp in state: %u\n", ms->sap_entity.sap_state);
+			return;
+		}
+		if(msg->num_params != 2){
+			LOGP(DSAP, LOGL_ERROR, "wrong number of parameters %u in APDU response\n", msg->num_params);
+			return;
+		}
+		ms->sap_entity.sap_state = SAP_IDLE;
+		if(sap_parse_result(&msg->params[0]) == 0){
+			/* back apdu resp to layer23 */
+			sap_apdu_resp(ms, msg->params[1].value, msg->params[1].len);
+			LOGP(DSAP, LOGL_INFO, "sap_apdu_resp called, sending data back to layer23\n");
+		}
+		break;
+	case SAP_ERROR_RESP:
+		if(ms->sap_entity.sap_state == SAP_CONNECTION_UNDER_NEGOTIATION){
+			ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
+		} else {
+			ms->sap_entity.sap_state = SAP_IDLE;
+		}
+		break;
+	default:
+		LOGP(DSAP, LOGL_ERROR, "got unknown or not implemented SAP msgid: %u\n", msg->id);
+		break;
+	}
+}
+
+static int sap_read(struct osmo_fd *fd)
+{
+	struct msgb *msg = NULL;
+	struct osmocom_ms *ms = (struct osmocom_ms *) fd->data;
+	uint8_t *sap_buffer;
+	ssize_t rc;
+
+	sap_buffer = talloc_zero_size(NULL, ms->sap_entity.max_msg_size);
+	if(!sap_buffer){
+		fprintf(stderr, "Failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	rc = read(fd->fd, &len, sizeof(len));
-	if (rc < sizeof(len)) {
+	rc = read(fd->fd, sap_buffer, ms->sap_entity.max_msg_size - 1);
+	if (rc < 0) {
 		fprintf(stderr, "SAP socket failed\n");
 		msgb_free(msg);
-		if (rc >= 0)
-			rc = -EIO;
 		sap_close(ms);
 		return rc;
 	}
-
-	len = ntohs(len);
-	if (len > GSM_SAP_LENGTH) {
-		LOGP(DSAP, LOGL_ERROR, "Length is too big: %u\n", len);
+	if(rc == 0) {
+		fprintf(stderr, "SAP socket closed by server\n");
 		msgb_free(msg);
-		return -EINVAL;
+		sap_close(ms);
+		return -ECONNREFUSED;
 	}
 
+	sap_buffer[rc] = 0;
+	LOGP(DSAP, LOGL_INFO, "Received %zd bytes: %s\n", rc, osmo_hexdump(sap_buffer, rc));
 
-	msg->l1h = msgb_put(msg, len);
-	rc = read(fd->fd, msg->l1h, msgb_l1len(msg));
-	if (rc != msgb_l1len(msg)) {
-		LOGP(DSAP, LOGL_ERROR, "Can not read data: len=%d rc=%d "
-		     "errno=%d\n", len, rc, errno);
-		msgb_free(msg);
-		return rc;
-	}
+	sap_parse_resp(ms, sap_buffer, rc);
 
-	if (ms->sap_entity.msg_handler)
-		ms->sap_entity.msg_handler(msg, ms);
+	talloc_free(sap_buffer);
 
+	if (ms->sap_entity.msg_handler){
+		ms->sap_entity.msg_handler(msg, ms);
+	}
 	return 0;
 }
 
 static int sap_write(struct osmo_fd *fd, struct msgb *msg)
 {
-	int rc;
+	ssize_t rc;
 
 	if (fd->fd <= 0)
 		return -EINVAL;
 
+	LOGP(DSAP, LOGL_INFO, "< %s\n", osmo_hexdump(msg->data, msg->len));
 	rc = write(fd->fd, msg->data, msg->len);
 	if (rc != msg->len) {
-		LOGP(DSAP, LOGL_ERROR, "Failed to write data: rc: %d\n", rc);
+		LOGP(DSAP, LOGL_ERROR, "Failed to write data: rc: %zd\n", rc);
 		return rc;
 	}
 
 	return 0;
 }
 
+static void sap_connect(struct osmocom_ms *ms)
+{
+	uint8_t buffer[3];
+	struct msgb *msg;
+	uint16_t size = ms->sap_entity.max_msg_size;
+	struct sap_param params[1];
+
+	params[0].id = SAP_MAX_MSG_SIZE;
+	params[0].len = 2;
+
+	if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED) {
+		LOGP(DSAP, LOGL_ERROR, "Attempting to connect while there is an active connection.\n");
+		return;
+	}
+
+	buffer[0] = (size >> 8) & 0xFF;
+	buffer[1] = size & 0xFF;
+	buffer[2] = 0;
+	params[0].value = buffer;
+
+	msg = sap_create_msg(SAP_CONNECT_REQ, 1, params);
+	if(!msg)
+		return;
+
+	osmosap_send(ms, msg);
+
+	ms->sap_entity.sap_state = SAP_CONNECTION_UNDER_NEGOTIATION;
+}
+
+static void sap_disconnect(struct osmocom_ms *ms)
+{
+	struct msgb *msg;
+	if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED && ms->sap_entity.sap_state != SAP_CONNECTION_UNDER_NEGOTIATION){
+		LOGP(DSAP, LOGL_ERROR, "Attempting to disconnect while no active connection.\n");
+		return;
+	}
+
+	msg = sap_create_msg(SAP_DISCONNECT_REQ, 0, NULL);
+	if(!msg)
+		return;
+
+	osmosap_send(ms, msg);
+
+	ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
+}
+
+static void sap_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
+{
+	struct msgb *msg;
+	struct sap_param params[1];
+
+	params[0].id = SAP_COMMAND_APDU;
+	params[0].len = len;
+	params[0].value = data;
+
+	if(ms->sap_entity.sap_state != SAP_IDLE){
+		LOGP(DSAP, LOGL_ERROR, "Attempting to send APDU request while not being idle.\n");
+		return;
+	}
+
+	msg = sap_create_msg(SAP_TRANSFER_APDU_REQ, 1, params);
+	if(!msg)
+		return;
+
+	osmosap_send(ms, msg);
+
+	ms->sap_entity.sap_state = SAP_PROCESSING_APDU_REQUEST;
+}
+
 int sap_open(struct osmocom_ms *ms, const char *socket_path)
 {
-	int rc;
+	ssize_t rc;
 	struct sockaddr_un local;
+	struct gsm_settings *set = &ms->settings;
 
 	ms->sap_wq.bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0);
 	if (ms->sap_wq.bfd.fd < 0) {
@@ -119,10 +512,10 @@  int sap_open(struct osmocom_ms *ms, const char *socket_path)
 	strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
 	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
 
-	rc = connect(ms->sap_wq.bfd.fd, (struct sockaddr *) &local,
-		     sizeof(local.sun_family) + strlen(local.sun_path));
+	rc = connect(ms->sap_wq.bfd.fd, (struct sockaddr *) &local, sizeof(local));
 	if (rc < 0) {
-		fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path);
+		fprintf(stderr, "Failed to connect to '%s'\n", local.sun_path);
+		set->sap_socket_path[0] = 0;
 		close(ms->sap_wq.bfd.fd);
 		return rc;
 	}
@@ -139,6 +532,8 @@  int sap_open(struct osmocom_ms *ms, const char *socket_path)
 		return rc;
 	}
 
+	sap_connect(ms);
+
 	return 0;
 }
 
@@ -147,43 +542,61 @@  int sap_close(struct osmocom_ms *ms)
 	if (ms->sap_wq.bfd.fd <= 0)
 		return -EINVAL;
 
+	sap_disconnect(ms);
 	close(ms->sap_wq.bfd.fd);
 	ms->sap_wq.bfd.fd = -1;
 	osmo_fd_unregister(&ms->sap_wq.bfd);
-	osmo_wqueue_clear(&ms->sap_wq);
+        osmo_wqueue_clear(&ms->sap_wq);
 
 	return 0;
 }
 
-int osmosap_send(struct osmocom_ms *ms, struct msgb *msg)
+/* same signature as in L1CTL, so it can be called from sim.c */
+int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
 {
-	uint16_t *len;
+	//LOGP(DSAP, LOGL_ERROR, "Received the following APDU from sim.c: %s\n" ,
+	//     osmo_hexdump(data, length));
+	sap_apdu(ms, data, length);
 
-	if (ms->sap_wq.bfd.fd <= 0)
-		return -EINVAL;
+	return 0;
+}
 
-	DEBUGP(DSAP, "Sending: '%s'\n", osmo_hexdump(msg->data, msg->len));
+/* register message handler for messages that are sent from L2->L3 */
+int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb)
+{
+	ms->sap_entity.msg_handler = cb;
 
-	if (msg->l1h != msg->data)
-		LOGP(DSAP, LOGL_ERROR, "Message SAP header != Message Data\n");
-	
-	/* prepend 16bit length before sending */
-	len = (uint16_t *) msgb_push(msg, sizeof(*len));
-	*len = htons(msg->len - sizeof(*len));
+	return 0;
+}
 
-	if (osmo_wqueue_enqueue(&ms->sap_wq, msg) != 0) {
-		LOGP(DSAP, LOGL_ERROR, "Failed to enqueue msg.\n");
-		msgb_free(msg);
-		return -1;
-	}
+int osmosap_sapsocket(struct osmocom_ms *ms, const char *path)
+{
+	struct gsm_settings *set = &ms->settings;
+	memset(set->sap_socket_path, 0, sizeof(set->sap_socket_path));
+	strncpy(set->sap_socket_path, path, sizeof(set->sap_socket_path) - 1);
 
 	return 0;
 }
 
-/* register message handler for messages that are sent from L2->L3 */
-int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb)
+/* init */
+int osmosap_init(struct osmocom_ms *ms)
 {
-	ms->sap_entity.msg_handler = cb;
+	struct osmosap_entity *sap = &ms->sap_entity;
+	int rc;
+
+	sap->sap_state = SAP_NOT_CONNECTED;
+	sap->max_msg_size = GSM_SAP_LENGTH;
+
+	LOGP(DSAP, LOGL_INFO, "init SAP client\n");
+
+	if(ms->settings.sap_socket_path){
+		rc = sap_open(ms, ms->settings.sap_socket_path);
+		if (rc < 0) {
+			fprintf(stderr, "Failed during sap_open(), no SAP based SIM reader\n");
+			ms->sap_wq.bfd.fd = -1;
+			return rc;
+		}
+	}
 
 	return 0;
 }
diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c
index 8c89cf0..7d1cf27 100644
--- a/src/host/layer23/src/common/sim.c
+++ b/src/host/layer23/src/common/sim.c
@@ -185,7 +185,17 @@  static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
 {
 	LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n",
 		data[0], data[1]);
-	l1ctl_tx_sim_req(ms, data, length);
+	//LOGP(DSIM, LOGL_INFO, "%s\n", osmo_hexdump(data, length));
+
+	/* adding SAP client support
+         * it makes more sense to do it here then in L1CTL */
+	if(ms->settings.sap_socket_path[0] == 0) {
+        	LOGP(DSIM, LOGL_INFO, "Using built-in SIM reader\n");
+		l1ctl_tx_sim_req(ms, data, length);
+	} else {
+		LOGP(DSIM, LOGL_INFO, "Using SAP backend\n");
+		osmosap_send_apdu(ms, data, length);
+	}
 	return 0;
 }
 
@@ -946,7 +956,7 @@  int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
 	case GSM1111_STAT_DL_ERROR:
 	case GSM1111_STAT_RESPONSE:
 	case GSM1111_STAT_RESPONSE_TOO:
-		LOGP(DSIM, LOGL_INFO, "command successfull\n");
+		LOGP(DSIM, LOGL_INFO, "command successful\n");
 		break;
 	default:
 		LOGP(DSIM, LOGL_INFO, "command failed\n");
diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c
index 9f28b0a..b473d4d 100644
--- a/src/host/layer23/src/mobile/app_mobile.c
+++ b/src/host/layer23/src/mobile/app_mobile.c
@@ -37,6 +37,9 @@ 
 #include <osmocom/bb/mobile/app_mobile.h>
 #include <osmocom/bb/mobile/mncc.h>
 #include <osmocom/bb/mobile/voice.h>
+/*adding SAP SIM reading */
+#include <osmocom/bb/common/sap_interface.h>
+
 #include <osmocom/vty/telnet_interface.h>
 
 #include <osmocom/core/msgb.h>
@@ -190,6 +193,8 @@  int mobile_init(struct osmocom_ms *ms)
 		T200_ACCH;
 	ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_usec = 0;
 	lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
+/* init SAP client before SIM card starts up */
+	osmosap_init(ms);
 
 	gsm_sim_init(ms);
 	gsm48_cc_init(ms);
diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c
index 8bcecba..a6dd082 100644
--- a/src/host/layer23/src/mobile/main.c
+++ b/src/host/layer23/src/mobile/main.c
@@ -69,7 +69,7 @@  int mobile_exit(struct osmocom_ms *ms, int force);
 
 
 const char *debug_default =
-	"DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM";
+	"DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DSAP";
 
 const char *openbsc_copyright =
 	"Copyright (C) 2008-2010 ...\n"