This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
Vadim Yanitskiy gerrit-no-reply at lists.osmocom.orgVadim Yanitskiy has uploaded this change for review. ( https://gerrit.osmocom.org/12441
Change subject: layer23/sap_interface.c: reimplement SAP interface
......................................................................
layer23/sap_interface.c: reimplement SAP interface
Change-Id: I77bb108615bb2c94c441568f195b04e0a5421643
---
M src/host/layer23/include/osmocom/bb/common/Makefile.am
M src/host/layer23/include/osmocom/bb/common/osmocom_data.h
A src/host/layer23/include/osmocom/bb/common/sap_fsm.h
M src/host/layer23/include/osmocom/bb/common/sap_interface.h
A src/host/layer23/include/osmocom/bb/common/sap_proto.h
M src/host/layer23/include/osmocom/bb/mobile/subscriber.h
M src/host/layer23/src/common/Makefile.am
A src/host/layer23/src/common/sap_fsm.c
M src/host/layer23/src/common/sap_interface.c
A src/host/layer23/src/common/sap_proto.c
M src/host/layer23/src/common/sim.c
M src/host/layer23/src/mobile/app_mobile.c
M src/host/layer23/src/mobile/subscriber.c
13 files changed, 1,342 insertions(+), 562 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/41/12441/1
diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am
index cd3437e..d66be98 100644
--- a/src/host/layer23/include/osmocom/bb/common/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am
@@ -1,2 +1,3 @@
noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h logging.h \
- networks.h gps.h sysinfo.h osmocom_data.h utils.h
+ networks.h gps.h sysinfo.h osmocom_data.h utils.h \
+ sap_proto.h sap_fsm.h sap_interface.h
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 0399f5f..9bbacd8 100644
--- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
+++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
@@ -23,8 +23,17 @@
#include <osmocom/bb/common/l1ctl.h>
struct osmosap_entity {
- uint8_t sap_state;
+ struct osmo_fsm_inst *fi;
uint16_t max_msg_size;
+ uint8_t card_status;
+
+ /* Optional (connection state) event call-backs */
+ sap_idle_cb_t idle_cb;
+ sap_rel_cb_t rel_cb;
+
+ /* Optional (ATR, APDU) event call-backs */
+ sap_apdu_cb_t apdu_cb;
+ sap_atr_cb_t atr_cb;
};
struct osmol1_entity {
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_fsm.h b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h
new file mode 100644
index 0000000..797e023
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <osmocom/bb/common/osmocom_data.h>
+
+/* How long should we wait for connection establishment */
+#define SAP_FSM_CONN_EST_TIMEOUT 5
+#define SAP_FSM_CONN_EST_T 0
+
+/* How long should we wait for connection release */
+#define SAP_FSM_CONN_REL_TIMEOUT 3
+#define SAP_FSM_CONN_REL_T 1
+
+/* How long should we wait for request to complete */
+#define SAP_FSM_PROC_REQ_TIMEOUT 5
+#define SAP_FSM_PROC_REQ_T 2
+
+enum sap_fsm_state {
+ SAP_STATE_NOT_CONNECTED,
+ SAP_STATE_CONNECTING,
+ SAP_STATE_DISCONNECTING, /* Auxiliary state (not from specs) */
+ SAP_STATE_WAIT_FOR_CARD, /* Auxiliary state (not from specs) */
+ SAP_STATE_IDLE,
+ SAP_STATE_PROC_ATR_REQ,
+ SAP_STATE_PROC_APDU_REQ,
+ SAP_STATE_PROC_RESET_REQ,
+ SAP_STATE_PROC_STATUS_REQ,
+ SAP_STATE_PROC_SET_TP_REQ,
+ SAP_STATE_PROC_POWERON_REQ,
+ SAP_STATE_PROC_POWEROFF_REQ,
+};
+
+int sap_fsm_alloc(struct osmocom_ms *ms);
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 78e3846..bde6822 100644
--- a/src/host/layer23/include/osmocom/bb/common/sap_interface.h
+++ b/src/host/layer23/include/osmocom/bb/common/sap_interface.h
@@ -1,98 +1,24 @@
#pragma once
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/bb/common/osmocom_data.h>
+
+typedef int (*sap_idle_cb_t)(struct osmocom_ms *ms);
+typedef int (*sap_rel_cb_t)(struct osmocom_ms *ms);
+
+typedef int (*sap_apdu_cb_t)(struct osmocom_ms *ms, struct msgb *msg);
+typedef int (*sap_atr_cb_t)(struct osmocom_ms *ms,
+ const uint8_t *atr, uint16_t atr_len);
+
+void sap_init(struct osmocom_ms *ms);
int sap_open(struct osmocom_ms *ms);
int sap_close(struct osmocom_ms *ms);
-int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length);
-int osmosap_init(struct osmocom_ms *ms);
+int _sap_close_sock(struct osmocom_ms *ms);
-enum osmosap_state {
- SAP_SOCKET_ERROR,
- SAP_NOT_CONNECTED,
- SAP_IDLE,
- SAP_CONNECTION_UNDER_NEGOTIATION,
- SAP_PROCESSING_ATR_REQUEST,
- SAP_PROCESSING_APDU_REQUEST
-};
+int sap_send_poweron_req(struct osmocom_ms *ms);
+int sap_send_poweroff_req(struct osmocom_ms *ms);
-/* Table 5.1: Message Overview */
-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
-};
-
-/* Table 5.15: List of Parameter IDs */
-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
-};
-
-/* Table 5.18: Possible values for ResultCode */
-enum sap_result_type {
- SAP_RESULT_OK_REQ_PROC_CORR = 0x00,
- SAP_RESULT_ERROR_NO_REASON = 0x01,
- SAP_RESULT_ERROR_CARD_NOT_ACC = 0x02,
- SAP_RESULT_ERROR_CARD_POWERED_OFF = 0x03,
- SAP_RESULT_ERROR_CARD_REMOVED = 0x04,
- SAP_RESULT_ERROR_CARD_POWERED_ON = 0x05,
- SAP_RESULT_ERROR_DATA_UNAVAIL = 0x06,
- SAP_RESULT_ERROR_NOT_SUPPORTED = 0x07,
-};
-
-/* Table 5.19: Possible values for StatusChange */
-enum sap_status_chg_type {
- SAP_STATUS_CHG_UNKNOWN_ERROR = 0x00,
- SAP_STATUS_CHG_CARD_RESET = 0x01,
- SAP_STATUS_CHG_CARD_NOT_ACC = 0x02,
- SAP_STATUS_CHG_CARD_REMOVED = 0x03,
- SAP_STATUS_CHG_CARD_INSERTED = 0x04,
- SAP_STATUS_CHG_CARD_RECOVERED = 0x05,
-};
-
-/* Table 5.16: Possible values for ConnectionStatus */
-enum sap_conn_status_type {
- SAP_CONN_STATUS_OK_READY = 0x00,
- SAP_CONN_STATUS_ERROR_CONN = 0x01,
- SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE = 0x02,
- SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE = 0x03,
- SAP_CONN_STATUS_OK_CALL = 0x04,
-};
-
-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;
-};
+int sap_send_atr_req(struct osmocom_ms *ms);
+int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len);
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_proto.h b/src/host/layer23/include/osmocom/bb/common/sap_proto.h
new file mode 100644
index 0000000..1c3b2d5
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/sap_proto.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+/* Table 5.1: Message Overview
+ * NOTE: messages are used as events for SAP FSM */
+enum sap_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
+};
+
+/* Table 5.15: List of Parameter IDs */
+enum sap_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
+};
+
+/* Table 5.18: Possible values for ResultCode */
+enum sap_result_type {
+ SAP_RESULT_OK_REQ_PROC_CORR = 0x00,
+ SAP_RESULT_ERROR_NO_REASON = 0x01,
+ SAP_RESULT_ERROR_CARD_NOT_ACC = 0x02,
+ SAP_RESULT_ERROR_CARD_POWERED_OFF = 0x03,
+ SAP_RESULT_ERROR_CARD_REMOVED = 0x04,
+ SAP_RESULT_ERROR_CARD_POWERED_ON = 0x05,
+ SAP_RESULT_ERROR_DATA_UNAVAIL = 0x06,
+ SAP_RESULT_ERROR_NOT_SUPPORTED = 0x07,
+};
+
+/* Table 5.19: Possible values for StatusChange */
+enum sap_card_status_type {
+ SAP_CARD_STATUS_UNKNOWN_ERROR = 0x00,
+ SAP_CARD_STATUS_CARD_RESET = 0x01,
+ SAP_CARD_STATUS_CARD_NOT_ACC = 0x02,
+ SAP_CARD_STATUS_CARD_REMOVED = 0x03,
+ SAP_CARD_STATUS_CARD_INSERTED = 0x04,
+ SAP_CARD_STATUS_CARD_RECOVERED = 0x05,
+};
+
+/* Table 5.16: Possible values for ConnectionStatus */
+enum sap_conn_status_type {
+ SAP_CONN_STATUS_OK_READY = 0x00,
+ SAP_CONN_STATUS_ERROR_CONN = 0x01,
+ SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE = 0x02,
+ SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE = 0x03,
+ SAP_CONN_STATUS_OK_CALL = 0x04,
+};
+
+extern const struct value_string sap_param_names[];
+extern const struct value_string sap_msg_names[];
+extern const struct value_string sap_result_names[];
+extern const struct value_string sap_card_status_names[];
+extern const struct value_string sap_conn_status_names[];
+
+/* Figure 5.2: Payload Coding */
+struct sap_param {
+ /* Parameter ID, see \ref enum sap_param_type */
+ uint8_t param_id;
+ /* Reserved for further use (shall be set to 0x00) */
+ uint8_t reserved[1];
+ /* Parameter length */
+ uint16_t length;
+ /* Parameter value (and optional padding) */
+ uint8_t value[0];
+} __attribute__((packed));
+
+/* Figure 5.1 Message Format */
+struct sap_message {
+ /* Message ID, see \ref enum sap_msg_type */
+ uint8_t msg_id;
+ /* Number of parameters */
+ uint8_t num_params;
+ /* Reserved for further use (shall be set to 0x00) */
+ uint8_t reserved[2];
+ /* Payload, see \ref struct sap_param */
+ uint8_t payload[0];
+} __attribute__((packed));
+
+#define GSM_SAP_LENGTH 300
+#define GSM_SAP_HEADROOM 32
+
+struct msgb *sap_msgb_alloc(uint8_t msg_id);
+const struct sap_message *sap_msg_parse(const uint8_t *buf, size_t buf_len);
+int sap_check_result_code(const struct sap_message *sap_msg);
+
+void sap_msgb_add_param(struct msgb *msg,
+ enum sap_param_type param_type,
+ uint16_t param_len, uint8_t *param_value);
+struct sap_param *sap_get_param(const struct sap_message *sap_msg,
+ enum sap_param_type param_type, uint16_t *param_len);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
index c747af9..37b6db7 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
@@ -93,6 +93,7 @@
int gsm_subscr_sapcard(struct osmocom_ms *ms);
int gsm_subscr_remove_sapcard(struct osmocom_ms *ms);
int gsm_subscr_simcard(struct osmocom_ms *ms);
+int subscr_sim_request(struct osmocom_ms *ms);
void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
int8_t mode);
int gsm_subscr_write_loci(struct osmocom_ms *ms);
diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am
index b76094c..a8d9f7e 100644
--- a/src/host/layer23/src/common/Makefile.am
+++ b/src/host/layer23/src/common/Makefile.am
@@ -2,5 +2,5 @@
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS)
noinst_LIBRARIES = liblayer23.a
-liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_interface.c \
+liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_fsm.c sap_proto.c sap_interface.c \
logging.c networks.c sim.c sysinfo.c gps.c l1ctl_lapdm_glue.c utils.c
diff --git a/src/host/layer23/src/common/sap_fsm.c b/src/host/layer23/src/common/sap_fsm.c
new file mode 100644
index 0000000..94a46a7
--- /dev/null
+++ b/src/host/layer23/src/common/sap_fsm.c
@@ -0,0 +1,679 @@
+/*
+ * SAP (SIM Access Profile) FSM definition
+ * based on Bluetooth SAP specification
+ *
+ * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/logging.h>
+
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/sap_fsm.h>
+
+/*! Send encoded SAP message to the Server
+ * \param[in] ms MS instance with active SAP connection
+ * \param[in] msg encoded SAP message buffer
+ * \returns 0 in case of success, negative in case of error
+ */
+static int sap_send_msgb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ int rc;
+
+ rc = osmo_wqueue_enqueue(&ms->sap_wq, msg);
+ if (rc) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to enqueue SAP message\n");
+ msgb_free(msg);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void sap_fsm_tear_down(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+
+ /* Flush buffers, close socket */
+ _sap_close_sock(ms);
+
+ /* Call release call-back (if present) */
+ if (ms->sap_entity.rel_cb)
+ ms->sap_entity.rel_cb(ms);
+
+ /* Trigger self-destruction :/ */
+ osmo_fsm_inst_free(ms->sap_entity.fi);
+ ms->sap_entity.fi = NULL;
+}
+
+static void sap_fsm_connect(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct msgb *msg;
+ uint16_t size;
+
+ /* Section 5.1.1, CONNECT_REQ */
+ msg = sap_msgb_alloc(SAP_CONNECT_REQ);
+ if (!msg)
+ return;
+
+ /* Section 4.1.1, start MaxMsgSize negotiation */
+ size = htons(ms->sap_entity.max_msg_size);
+ sap_msgb_add_param(msg, SAP_MAX_MSG_SIZE,
+ sizeof(size), (uint8_t *) &size);
+
+ sap_send_msgb(ms, msg);
+}
+
+static void sap_negotiate_msg_size(struct osmosap_entity *sap,
+ const struct sap_message *sap_msg)
+{
+ uint16_t size, param_len;
+ const char *cause = NULL;
+ struct sap_param *param;
+
+ param = sap_get_param(sap_msg, SAP_MAX_MSG_SIZE, ¶m_len);
+ if (!param) {
+ cause = "missing expected MaxMsgSize parameter";
+ goto error;
+ }
+ if (param_len != sizeof(size)) {
+ cause = "MaxMsgSize parameter has wrong length";
+ goto error;
+ }
+
+ /* Parse MaxMsgSize suggested by server */
+ size = ntohs((uint16_t *) param->value);
+ if (size > SAP_MAX_MSG_SIZE) {
+ cause = "suggested MaxMsgSize is too big for us";
+ goto error;
+ }
+
+ /* Attempt to initiate connection again */
+ sap->max_msg_size = size;
+ sap_fsm_connect(sap->fi, sap->fi->state);
+ return;
+
+error:
+ LOGP(DSAP, LOGL_ERROR, "MaxMsgSize negotiation failed: %s\n", cause);
+ osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+}
+
+static void sap_fsm_conn_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ const struct sap_message *sap_msg = (const struct sap_message *) data;
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t status;
+
+ /* Section 5.1.2, CONNECT_RESP */
+ param = sap_get_param(sap_msg, SAP_CONNECTION_STATUS, ¶m_len);
+ if (!param || param_len != sizeof(status)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory connection status\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ return;
+ }
+
+ /* Parse connection status */
+ status = param->value[0];
+
+ LOGP(DSAP, LOGL_INFO, "SAP connection status (0x%02x): %s\n",
+ status, get_value_string(sap_conn_status_names, status));
+
+ switch (status) {
+ case SAP_CONN_STATUS_OK_CALL:
+ ms->sap_entity.card_status = SAP_CARD_STATUS_CARD_NOT_ACC;
+ /* fall-through */
+ case SAP_CONN_STATUS_OK_READY:
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0);
+ break;
+
+ case SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE:
+ case SAP_CONN_STATUS_ERROR_CONN:
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ break;
+
+ /* Section 4.1.1, MaxMsgSize negotiation */
+ case SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE:
+ sap_negotiate_msg_size(&ms->sap_entity, sap_msg);
+ break;
+ }
+}
+
+static void sap_fsm_conn_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct msgb *msg;
+
+ LOGP(DSAP, LOGL_DEBUG, "Client initiated connection release\n");
+
+ /* We don't care about possible allocating / sending errors */
+ msg = sap_msgb_alloc(SAP_DISCONNECT_REQ);
+ if (msg != NULL)
+ sap_send_msgb(ms, msg);
+}
+
+static void sap_fsm_release_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ LOGP(DSAP, LOGL_DEBUG, "Client initiated release complete\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+}
+
+static void sap_fsm_idle_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ switch (prev_state) {
+ case SAP_STATE_CONNECTING:
+ case SAP_STATE_WAIT_FOR_CARD:
+ /* Sent ATR request (required by the specs) */
+ sap_send_atr_req((struct osmocom_ms *) fi->priv);
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+}
+
+static void sap_fsm_idle_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct msgb *msg = (struct msgb *) data;
+ enum sap_fsm_state state;
+ int rc;
+
+ /* Map event to the corresponding state */
+ switch (event) {
+ case SAP_TRANSFER_ATR_REQ:
+ state = SAP_STATE_PROC_ATR_REQ;
+ break;
+ case SAP_TRANSFER_APDU_REQ:
+ state = SAP_STATE_PROC_APDU_REQ;
+ break;
+ case SAP_RESET_SIM_REQ:
+ state = SAP_STATE_PROC_RESET_REQ;
+ break;
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ state = SAP_STATE_PROC_STATUS_REQ;
+ break;
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ state = SAP_STATE_PROC_SET_TP_REQ;
+ break;
+ case SAP_POWER_SIM_ON_REQ:
+ state = SAP_STATE_PROC_POWERON_REQ;
+ break;
+ case SAP_POWER_SIM_OFF_REQ:
+ state = SAP_STATE_PROC_POWEROFF_REQ;
+ break;
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ rc = sap_send_msgb(ms, msg);
+ if (rc)
+ return;
+
+ osmo_fsm_inst_state_chg(fi, state,
+ SAP_FSM_PROC_REQ_TIMEOUT, SAP_FSM_PROC_REQ_T);
+}
+
+static void sap_fsm_proc_atr_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ const struct sap_message *sap_msg = (const struct sap_message *) data;
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ int rc;
+
+ if (event != SAP_TRANSFER_ATR_RESP) {
+ LOGP(DSAP, LOGL_ERROR, "Event unhandled (yet)\n");
+ return;
+ }
+
+ /* We're done with ATR request now */
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+
+ /* Check the ResultCode */
+ rc = sap_check_result_code(sap_msg);
+ if (rc != SAP_RESULT_OK_REQ_PROC_CORR) {
+ LOGP(DSAP, LOGL_NOTICE, "Bad ResultCode: '%s'\n",
+ get_value_string(sap_result_names, rc));
+ return;
+ }
+
+ param = sap_get_param(sap_msg, SAP_ATR, ¶m_len);
+ if (!param) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_ATR));
+ return;
+ }
+
+ LOGP(DSAP, LOGL_DEBUG, "RX ATR response: %s\n",
+ osmo_hexdump(param->value, param_len));
+
+ /* Forward to ATR handler (if present) */
+ if (ms->sap_entity.atr_cb)
+ ms->sap_entity.atr_cb(ms, param->value, param_len);
+
+ /* We are ready to process APDU requests now */
+ if (ms->sap_entity.idle_cb)
+ ms->sap_entity.idle_cb(ms);
+}
+
+static void sap_fsm_proc_apdu_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ const struct sap_message *sap_msg = (const struct sap_message *) data;
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ struct msgb *msg;
+ uint8_t *apdu;
+ int rc;
+
+ if (event != SAP_TRANSFER_APDU_RESP) {
+ LOGP(DSAP, LOGL_ERROR, "Event unhandled (yet)\n");
+ return;
+ }
+
+ /* We're done with APDU request now */
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+
+ /* Check the ResultCode */
+ rc = sap_check_result_code(sap_msg);
+ if (rc != SAP_RESULT_OK_REQ_PROC_CORR) {
+ LOGP(DSAP, LOGL_NOTICE, "Bad ResultCode: '%s'\n",
+ get_value_string(sap_result_names, rc));
+ return;
+ }
+
+ param = sap_get_param(sap_msg, SAP_RESPONSE_APDU, ¶m_len);
+ if (!param) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_RESPONSE_APDU));
+ return;
+ }
+
+ /* Forward to APDU handler (if present) */
+ if (!ms->sap_entity.apdu_cb) {
+ LOGP(DSAP, LOGL_NOTICE, "No handler set, response APDU ignored\n");
+ return;
+ }
+
+ /* FIXME: why do we use this length?!? */
+ msg = msgb_alloc(GSM_SAP_LENGTH, "sim_apdu");
+ if (!msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory\n");
+ return;
+ }
+
+ apdu = msgb_put(msg, param_len);
+ memcpy(apdu, param->value, param_len);
+
+ /* Forward to APDU handler (if present) */
+ LOGP(DSAP, LOGL_DEBUG, "Forwarding APDU to the handler\n");
+ ms->sap_entity.apdu_cb(ms, msg);
+}
+
+/* Generates mask for a single state or event */
+#define S(x) (1 << x)
+
+/* Figure 4.13: Simplified State Machine */
+static const struct osmo_fsm_state sap_fsm_states[] = {
+ [SAP_STATE_NOT_CONNECTED] = {
+ .name = "NOT_CONNECTED",
+ .out_state_mask = S(SAP_STATE_CONNECTING),
+ .onenter = &sap_fsm_tear_down,
+ },
+ [SAP_STATE_CONNECTING] = {
+ .name = "CONNECTING",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_WAIT_FOR_CARD),
+ .in_event_mask = S(SAP_CONNECT_RESP),
+ .onenter = &sap_fsm_connect,
+ .action = &sap_fsm_conn_handler,
+ },
+ /* NOTE: this is a custom state (i.e. not defined by the specs).
+ * We need it in order to do release procedure correctly. */
+ [SAP_STATE_DISCONNECTING] = {
+ .name = "DISCONNECTING",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED),
+ .in_event_mask = S(SAP_DISCONNECT_RESP),
+ .onenter = &sap_fsm_conn_release,
+ .action = &sap_fsm_release_handler,
+ },
+ /* NOTE: this is a custom state (i.e. not defined by the specs).
+ * We need it in order to wait until SIM card becomes available.
+ * SAP_STATUS_IND event is handled by sap_fsm_allstate_action(). */
+ [SAP_STATE_WAIT_FOR_CARD] = {
+ .name = "WAIT_FOR_CARD",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_IDLE),
+ },
+ [SAP_STATE_IDLE] = {
+ .name = "IDLE",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_PROC_APDU_REQ)
+ | S(SAP_STATE_PROC_ATR_REQ)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_STATUS_REQ)
+ | S(SAP_STATE_PROC_SET_TP_REQ)
+ | S(SAP_STATE_PROC_POWERON_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_ATR_REQ)
+ | S(SAP_TRANSFER_APDU_REQ)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_TRANSFER_CARD_READER_STATUS_REQ)
+ | S(SAP_SET_TRANSPORT_PROTOCOL_REQ)
+ | S(SAP_POWER_SIM_ON_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .onenter = &sap_fsm_idle_enter,
+ .action = &sap_fsm_idle_handler,
+ },
+ [SAP_STATE_PROC_ATR_REQ] = {
+ .name = "PROC_ATR_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_ATR_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_proc_atr_handler,
+ },
+ [SAP_STATE_PROC_APDU_REQ] = {
+ .name = "PROC_APDU_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_APDU_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_proc_apdu_handler,
+ },
+ [SAP_STATE_PROC_RESET_REQ] = {
+ .name = "PROC_RESET_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_RESET_SIM_RESP)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ // .action = sap_fsm_proc_handler,
+ },
+ [SAP_STATE_PROC_STATUS_REQ] = {
+ .name = "PROC_STATUS_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_CARD_READER_STATUS_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ // .action = sap_fsm_proc_handler,
+ },
+ [SAP_STATE_PROC_SET_TP_REQ] = {
+ .name = "PROC_SET_TP_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE),
+ .in_event_mask = S(SAP_SET_TRANSPORT_PROTOCOL_RESP),
+ // .action = sap_fsm_proc_handler,
+ },
+ [SAP_STATE_PROC_POWERON_REQ] = {
+ .name = "PROC_POWERON_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_POWER_SIM_ON_RESP)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ // .action = sap_fsm_proc_handler,
+ },
+ [SAP_STATE_PROC_POWEROFF_REQ] = {
+ .name = "PROC_POWEROFF_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE),
+ .in_event_mask = S(SAP_POWER_SIM_OFF_RESP),
+ // .action = sap_fsm_proc_handler,
+ },
+};
+
+static void sap_fsm_handle_card_status_ind(struct osmo_fsm_inst *fi,
+ const struct sap_message *sap_msg)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t status;
+
+ param = sap_get_param(sap_msg, SAP_STATUS_CHANGE, ¶m_len);
+ if (!param || param_len != sizeof(status)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_STATUS_CHANGE));
+ return;
+ }
+
+ status = param->value[0];
+
+ if (ms->sap_entity.card_status != status) {
+ LOGP(DSAP, LOGL_NOTICE, "(SIM) card status change '%s' -> '%s'\n",
+ get_value_string(sap_card_status_names, ms->sap_entity.card_status),
+ get_value_string(sap_card_status_names, status));
+ ms->sap_entity.card_status = status;
+ }
+
+ switch (status) {
+ /* SIM card is ready */
+ case SAP_CARD_STATUS_CARD_RESET:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ /* SIM card has recovered after unaccessful state */
+ case SAP_CARD_STATUS_CARD_RECOVERED:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ /* SIM care inserted, we need to power it on */
+ case SAP_CARD_STATUS_CARD_INSERTED:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ sap_send_poweron_req(ms);
+ break;
+
+ case SAP_CARD_STATUS_UNKNOWN_ERROR:
+ case SAP_CARD_STATUS_CARD_NOT_ACC:
+ case SAP_CARD_STATUS_CARD_REMOVED:
+ default: /* Unknown card status */
+ if (fi->state != SAP_STATE_WAIT_FOR_CARD)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0);
+ break;
+ }
+}
+
+static void sap_fsm_allstate_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ switch (event) {
+ /* Disconnect indication initiated by the Server.
+ * FIXME: at the moment, immediate release is always assumed,
+ * but ideally we should check type of release (using *data) */
+ case SAP_DISCONNECT_IND:
+ /* This message may arrive in any of the sub-states of
+ * the "Connected" state (i.e. connection shall exist) */
+ if (fi->state < SAP_STATE_WAIT_FOR_CARD)
+ goto not_peritted;
+ /* fall-through */
+
+ /* Disconnect initiated by the Client */
+ case SAP_DISCONNECT_REQ:
+ /* If we have no active connection, tear-down immediately */
+ if (fi->state < SAP_STATE_WAIT_FOR_CARD) {
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_NOT_CONNECTED, 0, 0);
+ return;
+ }
+
+ /* Trigger Client-initiater connection release */
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_DISCONNECTING,
+ SAP_FSM_CONN_REL_TIMEOUT, SAP_FSM_CONN_REL_T);
+ break;
+
+ /* SIM status indication (inserted or ejected) */
+ case SAP_STATUS_IND:
+ /* This message may arrive in any of the sub-states of
+ * the "Connected" state (i.e. connection shall exist) */
+ if (fi->state < SAP_STATE_WAIT_FOR_CARD)
+ goto not_peritted;
+
+ sap_fsm_handle_card_status_ind(fi,
+ (const struct sap_message *) data);
+ break;
+
+ case SAP_ERROR_RESP:
+ if (fi->state == SAP_STATE_CONNECTING) {
+ /* Connection establishment error */
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_NOT_CONNECTED, 0, 0);
+ } else if (fi->state > SAP_STATE_IDLE) {
+ /* Error replaces any Request message */
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_IDLE, 0, 0);
+ } else {
+ /* Should not happen in general */
+ goto not_peritted;
+ }
+ break;
+
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ return;
+
+not_peritted:
+ LOGPFSML(fi, LOGL_NOTICE, "Event '%s' is not "
+ "permitted in state '%s', please fix!\n",
+ osmo_fsm_event_name(fi->fsm, event),
+ osmo_fsm_state_name(fi->fsm, fi->state));
+}
+
+static int sap_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ switch (fi->state) {
+ /* Connection establishment / release timeout */
+ case SAP_STATE_DISCONNECTING:
+ case SAP_STATE_CONNECTING:
+ LOGP(DSAP, LOGL_NOTICE, "Connection timeout\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ break;
+
+ /* Request processing timeout */
+ case SAP_STATE_PROC_ATR_REQ:
+ case SAP_STATE_PROC_APDU_REQ:
+ case SAP_STATE_PROC_RESET_REQ:
+ case SAP_STATE_PROC_STATUS_REQ:
+ case SAP_STATE_PROC_SET_TP_REQ:
+ case SAP_STATE_PROC_POWERON_REQ:
+ case SAP_STATE_PROC_POWEROFF_REQ:
+ LOGP(DSAP, LOGL_NOTICE, "Timeout waiting for '%s' to complete, "
+ "going back to IDLE\n", osmo_fsm_inst_state_name(fi));
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ default:
+ LOGP(DSAP, LOGL_ERROR, "Unhandled state '%s'\n",
+ osmo_fsm_inst_state_name(fi));
+ }
+
+ /* Do not tear-down FSM */
+ return 0;
+}
+
+static struct osmo_fsm sap_fsm_def = {
+ .name = "sap_fsm",
+ .log_subsys = DSAP,
+ .states = sap_fsm_states,
+ .num_states = ARRAY_SIZE(sap_fsm_states),
+ .event_names = sap_msg_names,
+ .timer_cb = &sap_fsm_timer_cb,
+ .allstate_action = &sap_fsm_allstate_action,
+ .allstate_event_mask = 0
+ | S(SAP_DISCONNECT_REQ)
+ | S(SAP_DISCONNECT_IND)
+ | S(SAP_STATUS_IND)
+ | S(SAP_ERROR_RESP),
+};
+
+int sap_fsm_alloc(struct osmocom_ms *ms)
+{
+ struct osmosap_entity *sap;
+
+ sap = &ms->sap_entity;
+ OSMO_ASSERT(sap->fi == NULL);
+
+ /* Register our FSM (if required) */
+ if (!osmo_fsm_find_by_name(sap_fsm_def.name))
+ OSMO_ASSERT(osmo_fsm_register(&sap_fsm_def) == 0);
+
+ /* Allocate an instance using ms as talloc context */
+ sap->fi = osmo_fsm_inst_alloc(&sap_fsm_def, ms,
+ ms, LOGL_DEBUG, ms->name);
+ if (!sap->fi) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP FSM\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c
index c31d07b..4ed457f 100644
--- a/src/host/layer23/src/common/sap_interface.c
+++ b/src/host/layer23/src/common/sap_interface.c
@@ -4,6 +4,7 @@
* (C) 2010,2018 by Harald Welte <laforge at gnumonks.org>
* (C) 2010 by Andreas Eversberg <jolly at eversberg.eu>
* (C) 2011 by Nico Golde <nico at ngolde.de>
+ * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com>
*
* All Rights Reserved
*
@@ -23,536 +24,272 @@
*
*/
-#include <osmocom/bb/common/osmocom_data.h>
-#include <osmocom/bb/common/logging.h>
-#include <osmocom/bb/common/sap_interface.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/socket.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <arpa/inet.h>
-
-#define _GNU_SOURCE
#include <unistd.h>
#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#define GSM_SAP_LENGTH 300
-#define GSM_SAP_HEADROOM 32
+#include <sys/socket.h>
-static void sap_connect(struct osmocom_ms *ms);
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
-/* Table 5.15: List of Parameter IDs */
-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" },
-};
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/logging.h>
-/* Table 5.1: Message Overview */
-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" },
-};
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/sap_fsm.h>
-/* Table 5.18: Possible values for ResultCode */
-static const struct value_string sap_result_names[] = {
- { SAP_RESULT_OK_REQ_PROC_CORR, "OK, request processed correctly" },
- { SAP_RESULT_ERROR_NO_REASON, "Error, no reason defined" },
- { SAP_RESULT_ERROR_CARD_NOT_ACC, "Error, card not accessible" },
- { SAP_RESULT_ERROR_CARD_POWERED_OFF, "Error, card (already) powered off" },
- { SAP_RESULT_ERROR_CARD_REMOVED, "Error, card removed" },
- { SAP_RESULT_ERROR_CARD_POWERED_ON, "Error, card already powered on" },
- { SAP_RESULT_ERROR_DATA_UNAVAIL, "Error, data not available" },
- { SAP_RESULT_ERROR_NOT_SUPPORTED, "Error, not supported "}
-};
-
-/* Table 5.19: Possible values for StatusChange */
-static const struct value_string sap_status_change_names[] = {
- { SAP_STATUS_CHG_UNKNOWN_ERROR, "Unknown Error" },
- { SAP_STATUS_CHG_CARD_RESET, "Card reset" },
- { SAP_STATUS_CHG_CARD_NOT_ACC, "Card not accessible" },
- { SAP_STATUS_CHG_CARD_REMOVED, "Card removed" },
- { SAP_STATUS_CHG_CARD_INSERTED, "Card inserted" },
- { SAP_STATUS_CHG_CARD_RECOVERED, "Card recovered" },
-};
-
-/* Table 5.16: Possible values for ConnectionStatus */
-static const struct value_string sap_status_names[] = {
- { SAP_CONN_STATUS_OK_READY, "OK, Server can fulfill requirements" },
- { SAP_CONN_STATUS_ERROR_CONN, "Error, Server unable to establish connection" },
- { SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE, "Error, Server does not support maximum message size" },
- { SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE, "Error, maximum message size by Client is too small" },
- { SAP_CONN_STATUS_OK_CALL, "OK, ongoing call" },
-};
-
-static struct msgb *sap_create_msg(uint8_t id, uint8_t num_params, struct sap_param *params)
+/*! Send ATR request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_atr_req(struct osmocom_ms *ms)
{
struct msgb *msg;
- uint8_t *msgp;
- uint8_t i, plen, padding = 0;
+ int rc;
- 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)
- 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] > ARRAY_SIZE(sap_result_names)){
- 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;
-
- msg = sap_parse_msg(data);
- if (!msg) {
- LOGP(DSAP, LOGL_ERROR, "Failed to parse SAP message\n");
- 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){
- LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory\n");
+ msg = sap_msgb_alloc(SAP_TRANSFER_ATR_REQ);
+ if (!msg)
return -ENOMEM;
- }
- rc = read(fd->fd, sap_buffer, ms->sap_entity.max_msg_size - 1);
- if (rc < 0) {
- LOGP(DSAP, LOGL_ERROR,"SAP socket failed\n");
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_TRANSFER_ATR_REQ, msg);
+ if (rc) {
msgb_free(msg);
- sap_close(ms);
return rc;
}
- if(rc == 0) {
- LOGP(DSAP, LOGL_NOTICE, "SAP socket closed by server\n");
- msgb_free(msg);
- sap_close(ms);
- return -ECONNREFUSED;
- }
-
- sap_buffer[rc] = 0;
- LOGP(DSAP, LOGL_INFO, "Received %zd bytes: %s\n", rc, osmo_hexdump(sap_buffer, rc));
-
- sap_parse_resp(ms, sap_buffer, rc);
-
- talloc_free(sap_buffer);
return 0;
}
-static int sap_write(struct osmo_fd *fd, struct msgb *msg)
+/*! Send APDU request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \param[in] apdu APDU to be send
+ * \param[in] apdu_len length of APDU
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = sap_msgb_alloc(SAP_TRANSFER_APDU_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ sap_msgb_add_param(msg, SAP_COMMAND_APDU, apdu_len, apdu);
+
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_TRANSFER_APDU_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! Send (SIM) power on request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_poweron_req(struct osmocom_ms *ms)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = sap_msgb_alloc(SAP_POWER_SIM_ON_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_POWER_SIM_ON_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! Send (SIM) power off request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_poweroff_req(struct osmocom_ms *ms)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = sap_msgb_alloc(SAP_POWER_SIM_OFF_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_POWER_SIM_OFF_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int sap_read_cb(struct osmo_fd *fd)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fd->data;
+ struct osmosap_entity *sap = &ms->sap_entity;
+ const struct sap_message *sap_msg;
+ uint8_t buf[GSM_SAP_LENGTH];
+ ssize_t rc;
+
+ /* Prevent buffer overflow */
+ OSMO_ASSERT(sap->max_msg_size <= GSM_SAP_LENGTH);
+
+ rc = read(fd->fd, buf, sap->max_msg_size);
+ if (rc < 0) {
+ LOGP(DSAP, LOGL_ERROR, "SAP socket failed\n");
+ rc = -EIO;
+ goto error;
+ }
+ if (rc == 0) {
+ LOGP(DSAP, LOGL_NOTICE, "SAP socket closed by server\n");
+ rc = -ECONNREFUSED;
+ goto error;
+ }
+
+ LOGP(DSAP, LOGL_DEBUG, "RX SAP message (len=%zd): %s\n",
+ rc, osmo_hexdump(buf, rc));
+
+ /* Parse SAP message */
+ sap_msg = sap_msg_parse(buf, rc);
+ if (!sap_msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to parse SAP message\n");
+ return -EINVAL;
+ }
+
+ /* Pass received message to our FSM using message
+ * type as the FSM event name */
+ osmo_fsm_inst_dispatch(sap->fi, sap_msg->msg_id, sap_msg);
+
+ return 0;
+
+error:
+ /* Immediately tear-down FSM */
+ osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ return rc;
+}
+
+static int sap_write_cb(struct osmo_fd *fd, struct msgb *msg)
{
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: %zd\n", rc);
+ LOGP(DSAP, LOGL_ERROR, "Failed to write data\n");
return rc;
}
+ LOGP(DSAP, LOGL_DEBUG, "TX SAP message (len=%u): %s\n",
+ msg->len, osmo_hexdump(msg->data, msg->len));
+
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 int sap_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
-{
- struct msgb *msg;
- struct sap_param params[1];
- int rc;
-
- 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 -EIO;
- }
-
- msg = sap_create_msg(SAP_TRANSFER_APDU_REQ, 1, params);
- if(!msg)
- return -ENOMEM;
-
- rc = osmosap_send(ms, msg);
- if (rc)
- return rc;
-
- ms->sap_entity.sap_state = SAP_PROCESSING_APDU_REQUEST;
-
- return 0;
-}
-
+/*! Establishes SAP connection to the Server,
+ * allocates SAP FSM, and triggers connection procedure.
+ * \param[in] ms MS instance with configured SAP socket path
+ * \returns 0 in case of success, negative in case of error
+ */
int sap_open(struct osmocom_ms *ms)
{
int rc;
+ LOGP(DSAP, LOGL_INFO, "Establishing SAP connection "
+ "(using socket '%s')\n", ms->settings.sap_socket_path);
+
rc = osmo_sock_unix_init_ofd(&ms->sap_wq.bfd, SOCK_STREAM, 0,
ms->settings.sap_socket_path, OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGP(DSAP, LOGL_ERROR, "Failed to create unix domain socket %s: %s\n",
ms->settings.sap_socket_path, strerror(-rc));
- ms->sap_entity.sap_state = SAP_SOCKET_ERROR;
return rc;
}
osmo_wqueue_init(&ms->sap_wq, 100);
ms->sap_wq.bfd.data = ms;
- ms->sap_wq.read_cb = sap_read;
- ms->sap_wq.write_cb = sap_write;
+ ms->sap_wq.read_cb = &sap_read_cb;
+ ms->sap_wq.write_cb = &sap_write_cb;
- sap_connect(ms);
+ /* Allocate a SAP FSM for a given ms */
+ rc = sap_fsm_alloc(ms);
+ if (rc) {
+ _sap_close_sock(ms);
+ return rc;
+ }
- return 0;
+ /* Initiate SAP connection with Server */
+ LOGP(DSAP, LOGL_DEBUG, "Connecting to the Server...\n");
+ return osmo_fsm_inst_state_chg(ms->sap_entity.fi, SAP_STATE_CONNECTING,
+ SAP_FSM_CONN_EST_TIMEOUT, SAP_FSM_CONN_EST_T);
}
+/*! Closes SAP connection with the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
int sap_close(struct osmocom_ms *ms)
{
+ if (ms->sap_entity.fi == NULL) {
+ LOGP(DSAP, LOGL_NOTICE, "No active SAP connection (no FSM)\n");
+ return -EINVAL;
+ }
+
+ LOGP(DSAP, LOGL_INFO, "Closing SAP connection\n");
+ return osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_DISCONNECT_REQ, NULL);
+}
+
+/*! Low-level function for closing SAP (socket) connection.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int _sap_close_sock(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);
return 0;
}
-/* 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)
-{
- //LOGP(DSAP, LOGL_ERROR, "Received the following APDU from sim.c: %s\n" ,
- // osmo_hexdump(data, length));
- return sap_apdu(ms, data, length);
-}
-
-/* init */
-int osmosap_init(struct osmocom_ms *ms)
+/*! Initiates SAP client state for a given MS. */
+void sap_init(struct osmocom_ms *ms)
{
struct osmosap_entity *sap = &ms->sap_entity;
LOGP(DSAP, LOGL_INFO, "init SAP client\n");
- sap->sap_state = SAP_NOT_CONNECTED;
+
+ /* Default MaxMsgSize (to be negotiated) */
sap->max_msg_size = GSM_SAP_LENGTH;
+ /* SIM card status is not known yet */
+ sap->card_status = SAP_CARD_STATUS_CARD_NOT_ACC;
- return 0;
+ /* Optional event call-backs */
+ sap->idle_cb = NULL;
+ sap->rel_cb = NULL;
+ sap->apdu_cb = NULL;
+ sap->atr_cb = NULL;
}
-
diff --git a/src/host/layer23/src/common/sap_proto.c b/src/host/layer23/src/common/sap_proto.c
new file mode 100644
index 0000000..b170eb6
--- /dev/null
+++ b/src/host/layer23/src/common/sap_proto.c
@@ -0,0 +1,271 @@
+/*
+ * SAP (SIM Access Profile) protocol definition
+ * based on Bluetooth SAP specification
+ *
+ * (C) 2011 by Nico Golde <nico at ngolde.de>
+ * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/logging.h>
+
+/* Table 5.15: List of Parameter IDs */
+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" },
+};
+
+/* Table 5.1: Message Overview */
+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" },
+};
+
+/* Table 5.18: Possible values for ResultCode */
+const struct value_string sap_result_names[] = {
+ { SAP_RESULT_OK_REQ_PROC_CORR, "OK, request processed correctly" },
+ { SAP_RESULT_ERROR_NO_REASON, "Error, no reason defined" },
+ { SAP_RESULT_ERROR_CARD_NOT_ACC, "Error, card not accessible" },
+ { SAP_RESULT_ERROR_CARD_POWERED_OFF, "Error, card (already) powered off" },
+ { SAP_RESULT_ERROR_CARD_REMOVED, "Error, card removed" },
+ { SAP_RESULT_ERROR_CARD_POWERED_ON, "Error, card already powered on" },
+ { SAP_RESULT_ERROR_DATA_UNAVAIL, "Error, data not available" },
+ { SAP_RESULT_ERROR_NOT_SUPPORTED, "Error, not supported "}
+};
+
+/* Table 5.19: Possible values for StatusChange */
+const struct value_string sap_card_status_names[] = {
+ { SAP_CARD_STATUS_UNKNOWN_ERROR, "Unknown Error" },
+ { SAP_CARD_STATUS_CARD_RESET, "Card reset" },
+ { SAP_CARD_STATUS_CARD_NOT_ACC, "Card not accessible" },
+ { SAP_CARD_STATUS_CARD_REMOVED, "Card removed" },
+ { SAP_CARD_STATUS_CARD_INSERTED, "Card inserted" },
+ { SAP_CARD_STATUS_CARD_RECOVERED, "Card recovered" },
+};
+
+/* Table 5.16: Possible values for ConnectionStatus */
+const struct value_string sap_conn_status_names[] = {
+ { SAP_CONN_STATUS_OK_READY, "OK, Server can fulfill requirements" },
+ { SAP_CONN_STATUS_ERROR_CONN, "Error, Server unable to establish connection" },
+ { SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE, "Error, Server does not support maximum message size" },
+ { SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE, "Error, maximum message size by Client is too small" },
+ { SAP_CONN_STATUS_OK_CALL, "OK, ongoing call" },
+};
+
+struct msgb *sap_msgb_alloc(uint8_t msg_id)
+{
+ struct sap_message *sap_msg;
+ struct msgb *msg;
+
+ msg = msgb_alloc(GSM_SAP_LENGTH, "sap_msg");
+ if (!msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP message\n");
+ return NULL;
+ }
+
+ sap_msg = (struct sap_message *) msgb_put(msg, sizeof(*sap_msg));
+ sap_msg->msg_id = msg_id;
+
+ return msg;
+}
+
+void sap_msgb_add_param(struct msgb *msg,
+ enum sap_param_type param_type,
+ uint16_t param_len, uint8_t *param_value)
+{
+ struct sap_message *sap_msg;
+ struct sap_param *param;
+ uint8_t padding;
+ uint8_t *buf;
+
+ /* Update number of parameters */
+ sap_msg = (struct sap_message *) msg->data;
+ sap_msg->num_params++;
+
+ /* Allocate a new parameter */
+ param = (struct sap_param *) msgb_put(msg, sizeof(*param));
+ param->param_id = param_type;
+ param->reserved[0] = 0x00;
+
+ /* Encode parameter value and length */
+ param->length = htons(param_len);
+ buf = msgb_put(msg, param_len);
+ memcpy(buf, param_value, param_len);
+
+ /* Optional padding */
+ padding = 4 - (param_len % 4);
+ if (padding) {
+ buf = msgb_put(msg, padding);
+ memset(buf, 0x00, padding);
+ }
+}
+
+struct sap_param *sap_get_param(const struct sap_message *sap_msg,
+ enum sap_param_type param_type, uint16_t *param_len)
+{
+ const uint8_t *ptr = sap_msg->payload;
+ struct sap_param *param = NULL;
+ uint16_t plen;
+ int i;
+
+ /* We assume that message is parsed already,
+ * so we don't check for buffer overflows */
+ for (i = 0; i < sap_msg->num_params; i++) {
+ /* Parse one parameter */
+ param = (struct sap_param *) ptr;
+ plen = ntohs(param->length);
+
+ /* Match against a given ID */
+ if (param->param_id == param_type) {
+ if (param_len != NULL)
+ *param_len = plen;
+ return param;
+ }
+
+ /* Shift pointer to the next parameter */
+ ptr += sizeof(*param) + plen;
+ /* Optional padding */
+ ptr += 4 - (plen % 4);
+ }
+
+ return NULL;
+}
+
+const struct sap_message *sap_msg_parse(const uint8_t *buf, size_t buf_len)
+{
+ const struct sap_message *sap_msg;
+ const uint8_t *ptr;
+ int i;
+
+ /* Message header is mandatory */
+ if (buf_len < sizeof(*sap_msg)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing SAP message header\n");
+ return NULL;
+ }
+
+ sap_msg = (const struct sap_message *) buf;
+ buf_len -= sizeof(*sap_msg);
+ ptr = sap_msg->payload;
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP message '%s' has %u parameter(s)\n",
+ get_value_string(sap_msg_names, sap_msg->msg_id),
+ sap_msg->num_params);
+
+ for (i = 0; i < sap_msg->num_params; i++) {
+ struct sap_param *param;
+ uint16_t param_len;
+ uint16_t offset;
+
+ /* Prevent buffer overflow */
+ if (buf_len < sizeof(*param))
+ goto malformed;
+
+ /* Parse one parameter */
+ param = (struct sap_param *) ptr;
+ param_len = ntohs(param->length);
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP parameter '%s' (len=%u): %s\n",
+ get_value_string(sap_param_names, param->param_id),
+ param_len, osmo_hexdump(param->value, param_len));
+
+ /* Calculate relative offset */
+ offset = sizeof(*param) + param_len;
+ offset += 4 - (param_len % 4); /* Optional padding */
+
+ /* Prevent buffer overflow */
+ if (offset > buf_len)
+ goto malformed;
+
+ buf_len -= offset;
+ ptr += offset;
+ }
+
+ return sap_msg;
+
+malformed:
+ LOGP(DSAP, LOGL_ERROR, "Malformed SAP message "
+ "(parameter %i/%u)\n", i + 1, sap_msg->num_params);
+ return NULL;
+}
+
+int sap_check_result_code(const struct sap_message *sap_msg)
+{
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t res_code;
+
+ param = sap_get_param(sap_msg, SAP_RESULT_CODE, ¶m_len);
+ if (!param || param_len != sizeof(res_code)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_RESULT_CODE));
+ return -EINVAL;
+ }
+
+ res_code = param->value[0];
+ if (res_code > ARRAY_SIZE(sap_result_names)) {
+ LOGP(DSAP, LOGL_ERROR, "Unknown SAP ResultCode=0x%02x\n", res_code);
+ return -EINVAL;
+ }
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP ResultCode is '%s'\n",
+ get_value_string(sap_result_names, res_code));
+
+ return res_code;
+}
diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c
index 9d14cd3..a63ae2a 100644
--- a/src/host/layer23/src/common/sim.c
+++ b/src/host/layer23/src/common/sim.c
@@ -205,7 +205,7 @@
* it makes more sense to do it here then in L1CTL */
if (ms->subscr.sim_type == GSM_SIM_TYPE_SAP) {
LOGP(DSIM, LOGL_INFO, "Using SAP backend\n");
- rc = osmosap_send_apdu(ms, data, length);
+ rc = sap_send_apdu(ms, data, length);
} else {
LOGP(DSIM, LOGL_INFO, "Using built-in SIM reader\n");
rc = l1ctl_tx_sim_req(ms, data, length);
diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c
index 17c0c76..1f473a7 100644
--- a/src/host/layer23/src/mobile/app_mobile.c
+++ b/src/host/layer23/src/mobile/app_mobile.c
@@ -200,7 +200,11 @@
lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
/* init SAP client before SIM card starts up */
- osmosap_init(ms);
+ sap_init(ms);
+
+ /* TODO: remove SIM card on sap_entity.rel_cb() */
+ ms->sap_entity.idle_cb = &subscr_sim_request;
+ ms->sap_entity.apdu_cb = &sim_apdu_resp;
gsm_sim_init(ms);
gsm48_cc_init(ms);
diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c
index b2be554..e8c0ae3 100644
--- a/src/host/layer23/src/mobile/subscriber.c
+++ b/src/host/layer23/src/mobile/subscriber.c
@@ -544,7 +544,7 @@
};
/* request file from SIM */
-static int subscr_sim_request(struct osmocom_ms *ms)
+int subscr_sim_request(struct osmocom_ms *ms)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index];
@@ -1275,6 +1275,7 @@
gsm_subscr_exit(ms);
gsm_subscr_init(ms);
+ subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
subscr->sim_type = GSM_SIM_TYPE_SAP;
sprintf(subscr->sim_name, "sap");
subscr->sim_valid = 1;
@@ -1297,8 +1298,6 @@
return rc;
}
-
- return 0;
}
/* Deattach sapcard */
--
To view, visit https://gerrit.osmocom.org/12441
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I77bb108615bb2c94c441568f195b04e0a5421643
Gerrit-Change-Number: 12441
Gerrit-PatchSet: 1
Gerrit-Owner: Vadim Yanitskiy <axilirator at gmail.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20181224/2381f59f/attachment.htm>