diff --git a/src/client/Makefile.am b/src/client/Makefile.am index f37126a..43b030f 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -7,7 +7,7 @@ AM_CFLAGS = -Wall -I$(top_srcdir)/include -I/$(top_builddir)/include -I$(top_src bin_PROGRAMS = osmo-remsim-client-st2 osmo-remsim-client-shell osmo_remsim_client_shell_SOURCES = user_shell.c remsim_client_main.c \ - remsim_client.c ../rspro_client_fsm.c ../debug.c + remsim_client.c main_fsm.c ../rspro_client_fsm.c ../debug.c osmo_remsim_client_shell_CFLAGS = $(AM_CFLAGS) osmo_remsim_client_shell_LDADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOABIS_LIBS) \ $(top_builddir)/src/libosmo-rspro.la @@ -19,15 +19,16 @@ bundle_DATA=PkgInfo bundlelinuxdir=$(bundledir)/Linux bundlelinux_LTLIBRARIES = libifd_remsim_client.la libifd_remsim_client_la_SOURCES = user_ifdhandler.c \ - remsim_client.c ../rspro_client_fsm.c ../debug.c + remsim_client.c main_fsm.c ../rspro_client_fsm.c ../debug.c libifd_remsim_client_la_CFLAGS = $(AM_CFLAGS) libifd_remsim_client_la_CPPFLAGS = $(PCSC_CFLAGS) libifd_remsim_client_la_LDFLAGS = -no-undefined libifd_remsim_client_la_LIBADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOABIS_LIBS) \ $(top_builddir)/src/libosmo-rspro.la -osmo_remsim_client_st2_SOURCES = simtrace2-remsim_client.c \ - ../rspro_client_fsm.c ../debug.c +osmo_remsim_client_st2_SOURCES = user_simtrace2.c remsim_client_main.c \ + remsim_client.c main_fsm.c ../rspro_client_fsm.c ../debug.c +osmo_remsim_client_st2_CPPFLAGS = -DUSB_SUPPORT osmo_remsim_client_st2_CFLAGS = $(AM_CFLAGS) osmo_remsim_client_st2_LDADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOABIS_LIBS) \ $(OSMOUSB_LIBS) $(OSMOSIMTRACE2_LIBS) \ diff --git a/src/client/client.h b/src/client/client.h index 0761255..b828882 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -10,9 +10,49 @@ #include "slotmap.h" #include "debug.h" +/*********************************************************************** + * frontend interface + ***********************************************************************/ + +struct bankd_client; + +struct frontend_phys_status { + struct { + /* all members can be 0 (inactive), 1 (active) or -1 (not supported/known) */ + int reset_active; + int vcc_present; + int clk_active; + int card_present; + } flags; + uint16_t voltage_mv; + uint8_t fi; + uint8_t di; + uint8_t wi; + uint8_t waiting_time; +}; + +struct frontend_pts { + const uint8_t *buf; + size_t len; +}; + +struct frontend_tpdu { + const uint8_t *buf; + size_t len; +}; + +/* API from generic core to frontend (modem/cardem) */ +int frontend_request_card_insert(struct bankd_client *bc); +int frontend_request_sim_remote(struct bankd_client *bc); +int frontend_request_modem_reset(struct bankd_client *bc); +int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len); +int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len); +int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts); +int frontend_append_script_env(struct bankd_client *bc, char **env, size_t max_env); + /* main.c */ -struct cardem_inst; +struct osmo_st2_cardem_inst; #define ATR_SIZE_MAX 55 struct client_config { @@ -48,6 +88,8 @@ struct bankd_client { struct rspro_server_conn srv_conn; /* connection to the remsim-bankd (data) */ struct rspro_server_conn bankd_conn; + /* CLIENT_MAIN fsm */ + struct osmo_fsm_inst *main_fi; /* remote component ID */ struct app_comp_id peer_comp_id; @@ -55,17 +97,43 @@ struct bankd_client { struct bank_slot bankd_slot; struct client_config *cfg; - struct cardem_inst *cardem; + struct osmo_st2_cardem_inst *cardem; + struct frontend_phys_status last_status; void *data; }; #define srvc2bankd_client(srvc) container_of(srvc, struct bankd_client, srv_conn) #define bankdc2bankd_client(bdc) container_of(bdc, struct bankd_client, bankd_conn) -struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software); +struct client_config *client_config_init(void *ctx); +struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software, + struct client_config *cfg); void remsim_client_set_clslot(struct bankd_client *bc, int client_id, int slot_nr); - -extern int client_user_bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu); - extern int client_user_main(struct bankd_client *g_client); + + +/*********************************************************************** + * main FSM + ***********************************************************************/ + +enum main_fsm_event { + MF_E_SRVC_CONNECTED, /* connection to server established (TCP + RSPRO level) */ + MF_E_SRVC_LOST, /* connection to server was lost */ + MF_E_SRVC_CONFIG_BANK, /* server instructs us to connect to bankd/slot */ + MF_E_SRVC_RESET_REQ, /* RsproPDUchoice_PR_ResetStateReq */ + + MF_E_BANKD_CONNECTED, /* connection to bankd established (TCP + RSPRO level) */ + MF_E_BANKD_LOST, /* connection to bankd was lost */ + MF_E_BANKD_TPDU, /* RsproPDUchoice_PR_tpduCardToModem */ + MF_E_BANKD_ATR, /* RsproPDUchoice_PR_setAtrReq */ + MF_E_BANKD_SLOT_STATUS, /* bankSlotStatusInd */ + + MF_E_MDM_STATUS_IND, /* status from modem/cardem */ + MF_E_MDM_PTS_IND, /* PTS indication from modem/cardem */ + MF_E_MDM_TPDU, /* TPDU from modem/cardem */ +}; +struct osmo_fsm_inst *main_fsm_alloc(void *ctx, struct bankd_client *bc); + + + diff --git a/src/client/main_fsm.c b/src/client/main_fsm.c new file mode 100644 index 0000000..b94af26 --- /dev/null +++ b/src/client/main_fsm.c @@ -0,0 +1,377 @@ +/* (C) 2020 by Harald Welte + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "rspro_util.h" +#include "client.h" +#include "debug.h" + +#define S(x) (1 << (x)) + +/***********************************************************************/ + +/* build the (additional) environment for executing a script */ +static char **build_script_env(struct bankd_client *bc, const char *cause) +{ + char **env = talloc_zero_size(bc, 256*sizeof(char *)); + int rc, i = 0; + + if (!env) + return NULL; + + env[i++] = talloc_asprintf(env, "REMSIM_CLIENT_VERSION=%s", VERSION); + + env[i++] = talloc_asprintf(env, "REMSIM_SERVER_ADDR=%s:%u", + bc->srv_conn.server_host, bc->srv_conn.server_port); + env[i++] = talloc_asprintf(env, "REMSIM_SERVER_STATE=%s", + osmo_fsm_inst_state_name(bc->srv_conn.fi)); + + env[i++] = talloc_asprintf(env, "REMSIM_BANKD_ADDR=%s:%u", + bc->bankd_conn.server_host, bc->bankd_conn.server_port); + env[i++] = talloc_asprintf(env, "REMSIM_BANKD_STATE=%s", + osmo_fsm_inst_state_name(bc->bankd_conn.fi)); + + + if (bc->srv_conn.clslot) { + env[i++] = talloc_asprintf(env, "REMSIM_CLIENT_SLOT=%lu:%lu", + bc->srv_conn.clslot->clientId, + bc->srv_conn.clslot->slotNr); + } + env[i++] = talloc_asprintf(env, "REMSIM_BANKD_SLOT=%u:%u", + bc->bankd_slot.bank_id, bc->bankd_slot.slot_nr); + + env[i++] = talloc_asprintf(env, "REMSIM_SIM_VCC=%u", bc->last_status.flags.vcc_present); + env[i++] = talloc_asprintf(env, "REMSIM_SIM_RST=%u", bc->last_status.flags.reset_active); + /* TODO: SIM card state CLK */ + + env[i++] = talloc_asprintf(env, "REMSIM_CAUSE=%s", cause); + + /* ask frontend to append any frontend-speccific additional environment vars */ + rc = frontend_append_script_env(bc, env+i, 256-i); + if (rc > 0) + i += rc; + + /* terminate last entry */ + env[i++] = NULL; + return env; +} + +static int call_script(struct bankd_client *bc, const char *cause) +{ + char **env, *cmd; + int rc; + + if (!bc->cfg->event_script) + return 0; + + env = build_script_env(bc, cause); + if (!env) + return -ENOMEM; + + cmd = talloc_asprintf(env, "%s %s", bc->cfg->event_script, cause); + if (!cmd) { + talloc_free(env); + return -ENOMEM; + } + + rc = osmo_system_nowait(cmd, osmo_environment_whitelist, env); + talloc_free(env); + + return rc; +} + + +/***********************************************************************/ + + +enum main_fsm_state { + MF_ST_INIT, + MF_ST_UNCONFIGURED, /* waiting for configuration from server */ + MF_ST_WAIT_BANKD, /* configured; waiting for bankd conn */ + MF_ST_OPERATIONAL, /* fully operational (configured + bankd conn live */ +}; + +static const struct value_string main_fsm_event_names[] = { + OSMO_VALUE_STRING(MF_E_SRVC_CONNECTED), + OSMO_VALUE_STRING(MF_E_SRVC_LOST), + OSMO_VALUE_STRING(MF_E_SRVC_CONFIG_BANK), + OSMO_VALUE_STRING(MF_E_SRVC_RESET_REQ), + OSMO_VALUE_STRING(MF_E_BANKD_CONNECTED), + OSMO_VALUE_STRING(MF_E_BANKD_LOST), + OSMO_VALUE_STRING(MF_E_BANKD_TPDU), + OSMO_VALUE_STRING(MF_E_BANKD_ATR), + OSMO_VALUE_STRING(MF_E_BANKD_SLOT_STATUS), + OSMO_VALUE_STRING(MF_E_MDM_STATUS_IND), + OSMO_VALUE_STRING(MF_E_MDM_PTS_IND), + OSMO_VALUE_STRING(MF_E_MDM_TPDU), + { 0, NULL } +}; + +static void main_st_operational(struct osmo_fsm_inst *fi, uint32_t event, void *data); + +static void main_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bankd_client *bc = (struct bankd_client *) fi->priv; + + switch (event) { + case MF_E_SRVC_CONNECTED: + osmo_fsm_inst_state_chg(fi, MF_ST_UNCONFIGURED, 0, 0); + call_script(bc, "event-server-connect"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void main_st_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct bankd_client *bc = (struct bankd_client *) fi->priv; + /* we might be called from a 'higher' state such as operational; clean up */ + osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_DISCONNECT, NULL); +} + +static void main_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case MF_E_SRVC_CONFIG_BANK: + /* same treatment as below */ + main_st_operational(fi, event, data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void main_st_wait_bankd(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bankd_client *bc = (struct bankd_client *) fi->priv; + + switch (event) { + case MF_E_SRVC_CONFIG_BANK: + /* same treatment as below */ + main_st_operational(fi, event, data); + break; + case MF_E_BANKD_CONNECTED: + osmo_fsm_inst_state_chg(fi, MF_ST_OPERATIONAL, 0, 0); + call_script(bc, "event-bankd-connect"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void main_st_operational_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct bankd_client *bc = (struct bankd_client *) fi->priv; + + /* Simulate card-insert to modem */ + frontend_request_card_insert(bc); + call_script(bc, "request-card-insert"); + + /* Select remote (forwarded) SIM */ + frontend_request_sim_remote(bc); + call_script(bc, "request-sim-remote"); + + /* Set the ATR */ + frontend_handle_set_atr(bc, bc->cfg->atr.data, bc->cfg->atr.len); + + /* Reset the modem */ + frontend_request_modem_reset(bc); + call_script(bc, "request-modem-reset"); +} + +static void main_st_operational(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bankd_client *bc = (struct bankd_client *) fi->priv; + struct frontend_phys_status *pstatus = NULL; + struct frontend_pts *pts = NULL; + struct frontend_tpdu *tpdu = NULL; + RsproPDU_t *pdu_rx = NULL; + RsproPDU_t *resp; + BankSlot_t bslot; + + switch (event) { + case MF_E_BANKD_LOST: + osmo_fsm_inst_state_chg(fi, MF_ST_WAIT_BANKD, 0, 0); + break; + case MF_E_SRVC_CONFIG_BANK: + pdu_rx = data; + OSMO_ASSERT(pdu_rx); + OSMO_ASSERT(pdu_rx->msg.present == RsproPDUchoice_PR_configClientBankReq); + /* store/set the bankd ip/port as instructed by the server */ + osmo_talloc_replace_string(bc, &bc->bankd_conn.server_host, + rspro_IpAddr2str(&pdu_rx->msg.choice.configClientBankReq.bankd.ip)); + bc->bankd_conn.server_port = pdu_rx->msg.choice.configClientBankReq.bankd.port; + rspro2bank_slot(&bc->bankd_slot, &pdu_rx->msg.choice.configClientBankReq.bankSlot); + /* bankd port 0 is a magic value to indicate "no bankd" */ + if (bc->bankd_conn.server_port == 0) + osmo_fsm_inst_state_chg(fi, MF_ST_UNCONFIGURED, 0, 0); + else { + osmo_fsm_inst_state_chg(fi, MF_ST_WAIT_BANKD, 0, 0); + /* TODO: do we need to disconnect before? */ + osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_ESTABLISH, NULL); + } + /* send response to server */ + resp = rspro_gen_ConfigClientBankRes(ResultCode_ok); + server_conn_send_rspro(&bc->srv_conn, resp); + call_script(bc, "event-config-bankd"); + break; + case MF_E_BANKD_TPDU: + pdu_rx = data; + OSMO_ASSERT(pdu_rx); + OSMO_ASSERT(pdu_rx->msg.present == RsproPDUchoice_PR_tpduCardToModem); + /* forward to modem/cardem (via API) */ + frontend_handle_card2modem(bc, pdu_rx->msg.choice.tpduCardToModem.data.buf, + pdu_rx->msg.choice.tpduCardToModem.data.size); + /* response happens indirectly via tpduModemToCard */ + break; + case MF_E_BANKD_ATR: + pdu_rx = data; + OSMO_ASSERT(pdu_rx); + OSMO_ASSERT(pdu_rx->msg.present == RsproPDUchoice_PR_setAtrReq); + /* forward to modem/cardem (via API) */ + frontend_handle_set_atr(bc, pdu_rx->msg.choice.setAtrReq.atr.buf, + pdu_rx->msg.choice.setAtrReq.atr.size); + /* send response to bankd */ + resp = rspro_gen_SetAtrRes(ResultCode_ok); + server_conn_send_rspro(&bc->bankd_conn, resp); + break; + case MF_E_BANKD_SLOT_STATUS: + pdu_rx = data; + OSMO_ASSERT(pdu_rx); + OSMO_ASSERT(pdu_rx->msg.present == RsproPDUchoice_PR_bankSlotStatusInd); + /* forward to modem/cardem (via API) */ + frontend_handle_slot_status(bc, &pdu_rx->msg.choice.bankSlotStatusInd.slotPhysStatus); + break; + case MF_E_MDM_STATUS_IND: + pstatus = data; + OSMO_ASSERT(pstatus); + /* forward to bankd */ + bank_slot2rspro(&bslot, &bc->bankd_slot); + resp = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot, + pstatus->flags.reset_active, + pstatus->flags.vcc_present, + pstatus->flags.clk_active, + pstatus->flags.card_present); + server_conn_send_rspro(&bc->bankd_conn, resp); + if (!memcmp(&bc->last_status.flags, &pstatus->flags, sizeof(pstatus->flags))) + call_script(bc, "event-modem-status"); + bc->last_status = *pstatus; + break; + case MF_E_MDM_PTS_IND: + pts = data; + OSMO_ASSERT(pts); + /* forward to bankd? */ + break; + case MF_E_MDM_TPDU: + tpdu = data; + OSMO_ASSERT(tpdu); + /* forward to bankd */ + bank_slot2rspro(&bslot, &bc->bankd_slot); + resp = rspro_gen_TpduModem2Card(bc->srv_conn.clslot, &bslot, tpdu->buf, tpdu->len); + server_conn_send_rspro(&bc->bankd_conn, resp); + break; + default: + OSMO_ASSERT(0); + } +} + +static void main_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case MF_E_SRVC_LOST: + /* should we do anything? The SRVC fsm will take care of reconnect, and we + * can continue to talk to the bankd without any trouble... */ + break; + case MF_E_SRVC_RESET_REQ: + osmo_fsm_inst_state_chg(fi, MF_ST_UNCONFIGURED, 0, 0); + break; + default: + OSMO_ASSERT(0); + } +} + + +static const struct osmo_fsm_state main_fsm_states[] = { + [MF_ST_INIT] = { + .name = "INIT", + .in_event_mask = S(MF_E_SRVC_CONNECTED), + .out_state_mask = S(MF_ST_UNCONFIGURED), + .action = main_st_init, + }, + [MF_ST_UNCONFIGURED] = { + .name = "UNCONFIGURED", + .in_event_mask = S(MF_E_SRVC_CONFIG_BANK), + .out_state_mask = S(MF_ST_INIT) | S(MF_ST_WAIT_BANKD), + .action = main_st_unconfigured, + .onenter = main_st_unconfigured_onenter, + }, + [MF_ST_WAIT_BANKD] = { + .name = "WAIT_BANKD", + .in_event_mask = S(MF_E_SRVC_CONFIG_BANK) | S(MF_E_BANKD_CONNECTED), + .out_state_mask = S(MF_ST_INIT) | S(MF_ST_UNCONFIGURED) | S(MF_ST_OPERATIONAL), + .action = main_st_wait_bankd, + }, + [MF_ST_OPERATIONAL] = { + .name = "OPERATIONAL", + .in_event_mask = S(MF_E_SRVC_CONFIG_BANK) | + S(MF_E_BANKD_LOST) | + S(MF_E_BANKD_TPDU) | + S(MF_E_BANKD_ATR) | + S(MF_E_BANKD_SLOT_STATUS) | + S(MF_E_MDM_STATUS_IND) | + S(MF_E_MDM_PTS_IND) | + S(MF_E_MDM_TPDU), + .out_state_mask = S(MF_ST_INIT) | S(MF_ST_UNCONFIGURED) | S(MF_ST_WAIT_BANKD), + .action = main_st_operational, + .onenter = main_st_operational_onenter, + }, +}; + +static struct osmo_fsm client_main_fsm = { + .name = "CLIENT_MAIN", + .states = main_fsm_states, + .num_states = ARRAY_SIZE(main_fsm_states), + .allstate_event_mask = S(MF_E_SRVC_LOST) | S(MF_E_SRVC_RESET_REQ), + .allstate_action = main_allstate_action, + .log_subsys = DMAIN, + .event_names = main_fsm_event_names, +}; + +struct osmo_fsm_inst *main_fsm_alloc(void *ctx, struct bankd_client *bc) +{ + return osmo_fsm_inst_alloc(&client_main_fsm, ctx, bc, LOGL_DEBUG, "main"); +} + +static __attribute((constructor)) void on_dso_load_main_fsm(void) +{ + OSMO_ASSERT(osmo_fsm_register(&client_main_fsm) == 0); +} diff --git a/src/client/remsim_client.c b/src/client/remsim_client.c index dd78c8e..182894f 100644 --- a/src/client/remsim_client.c +++ b/src/client/remsim_client.c @@ -20,6 +20,10 @@ * */ +/* This file contains code shared among all remsim client applications, + * including the ifd-handler, which is not an executable program with a main() + * function or command line parsing, but a shared library */ + #include #include @@ -33,8 +37,38 @@ #include "client.h" #include "debug.h" +struct client_config *client_config_init(void *ctx) +{ + struct client_config *cfg = talloc_zero(ctx, struct client_config); + if (!cfg) + return NULL; + + cfg->server_host = talloc_strdup(cfg, "127.0.0.1"); + cfg->server_port = 9998; + cfg->client_id = -1; + cfg->client_slot = -1; + cfg->gsmtap_host = talloc_strdup(cfg, "127.0.0.1"); + cfg->keep_running = false; + + cfg->usb.vendor_id = -1; + cfg->usb.product_id = -1; + cfg->usb.config_id = -1; + cfg->usb.if_num = -1; + cfg->usb.altsetting = 0; + cfg->usb.addr = -1; + cfg->usb.path = NULL; + + cfg->atr.data[0] = 0x3B; + cfg->atr.data[1] = 0x00; // the shortest simplest ATR possible + cfg->atr.len = 2; + + return cfg; +}; + static int bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu) { + struct bankd_client *bc = bankdc2bankd_client(bankdc); + switch (pdu->msg.present) { case RsproPDUchoice_PR_connectClientRes: /* Store 'identity' of bankd to in peer_comp_id */ @@ -42,8 +76,11 @@ static int bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *p osmo_fsm_inst_dispatch(bankdc->fi, SRVC_E_CLIENT_CONN_RES, (void *) pdu); break; case RsproPDUchoice_PR_tpduCardToModem: + return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_TPDU, (void *) pdu); case RsproPDUchoice_PR_setAtrReq: - return client_user_bankd_handle_rx(bankdc, pdu); + return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_ATR, (void *) pdu); + case RsproPDUchoice_PR_bankSlotStatusInd: + return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_SLOT_STATUS, (void *) pdu); default: LOGPFSML(bankdc->fi, LOGL_ERROR, "Unknown/Unsupported RSPRO PDU %s\n", rspro_msgt_name(pdu)); @@ -78,19 +115,7 @@ static int srvc_handle_rx(struct rspro_server_conn *srvc, const RsproPDU_t *pdu) server_conn_send_rspro(srvc, resp); break; case RsproPDUchoice_PR_configClientBankReq: - /* store/set the bankd ip/port as instructed by the server */ - osmo_talloc_replace_string(bc, &bc->bankd_conn.server_host, - rspro_IpAddr2str(&pdu->msg.choice.configClientBankReq.bankd.ip)); - rspro2bank_slot(&bc->bankd_slot, &pdu->msg.choice.configClientBankReq.bankSlot); - bc->bankd_conn.server_port = pdu->msg.choice.configClientBankReq.bankd.port; - /* bankd port 0 is a magic value to indicate "no bankd" */ - if (bc->bankd_conn.server_port == 0) - osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_DISCONNECT, NULL); - else - osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_ESTABLISH, NULL); - /* send response to server */ - resp = rspro_gen_ConfigClientBankRes(ResultCode_ok); - server_conn_send_rspro(srvc, resp); + osmo_fsm_inst_dispatch(bc->main_fi, MF_E_SRVC_CONFIG_BANK, (void *) pdu); break; default: LOGPFSML(srvc->fi, LOGL_ERROR, "Unknown/Unsupported RSPRO PDU type: %s\n", @@ -101,7 +126,8 @@ static int srvc_handle_rx(struct rspro_server_conn *srvc, const RsproPDU_t *pdu) return 0; } -struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software) +struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software, + struct client_config *cfg) { struct bankd_client *bc = talloc_zero(ctx, struct bankd_client); struct rspro_server_conn *srvc, *bankdc; @@ -110,10 +136,20 @@ struct bankd_client *remsim_client_create(void *ctx, const char *name, const cha if (!bc) return NULL; + bc->cfg = cfg; + + bc->main_fi = main_fsm_alloc(bc, bc); + if (!bc->main_fi) { + fprintf(stderr, "Unable to create main client FSM: %s\n", strerror(errno)); + exit(1); + } + + remsim_client_set_clslot(bc, cfg->client_id, cfg->client_slot); + /* create and [attempt to] establish connection to remsim-server */ srvc = &bc->srv_conn; - srvc->server_host = "localhost"; - srvc->server_port = 9998; + srvc->server_host = cfg->server_host; + srvc->server_port = cfg->server_port; srvc->handle_rx = srvc_handle_rx; srvc->own_comp_id.type = ComponentType_remsimClient; OSMO_STRLCPY_ARRAY(srvc->own_comp_id.name, name); @@ -125,6 +161,9 @@ struct bankd_client *remsim_client_create(void *ctx, const char *name, const cha fprintf(stderr, "Unable to create Server conn FSM: %s\n", strerror(errno)); exit(1); } + osmo_fsm_inst_change_parent(srvc->fi, bc->main_fi, MF_E_SRVC_LOST); + srvc->parent_conn_evt = MF_E_SRVC_CONNECTED; + srvc->parent_disc_evt = MF_E_SRVC_LOST; bankdc = &bc->bankd_conn; /* server_host / server_port are configured from remsim-server */ @@ -136,6 +175,9 @@ struct bankd_client *remsim_client_create(void *ctx, const char *name, const cha exit(1); } osmo_fsm_inst_update_id(bankdc->fi, "bankd"); + osmo_fsm_inst_change_parent(bankdc->fi, bc->main_fi, MF_E_BANKD_LOST); + bankdc->parent_conn_evt = MF_E_BANKD_CONNECTED; + bankdc->parent_disc_evt = MF_E_BANKD_LOST; return bc; } diff --git a/src/client/remsim_client_main.c b/src/client/remsim_client_main.c index 95eb089..6587e6b 100644 --- a/src/client/remsim_client_main.c +++ b/src/client/remsim_client_main.c @@ -23,27 +23,56 @@ static void printf_help() { printf( " -h --help Print this help message\n" + " -v --version Print program version\n" " -i --server-ip A.B.C.D remsim-server IP address\n" " -p --server-port 13245 remsim-server TCP port\n" - " -i --client-id <0-65535> RSPRO ClientId of this client\n" + " -c --client-id <0-65535> RSPRO ClientId of this client\n" " -n --client-slot <0-65535> RSPRO SlotNr of this client\n" + " -e --event-script event script to be called by client\n" +#ifdef USB_SUPPORT + " -V --usb-vendor VENDOR_ID\n" + " -P --usb-product PRODUCT_ID\n" + " -C --usb-config CONFIG_ID\n" + " -I --usb-interface INTERFACE_ID\n" + " -S --usb-altsetting ALTSETTING_ID\n" + " -A --usb-address ADDRESS\n" + " -H --usb-path PATH\n" +#endif ); } -static void handle_options(struct bankd_client *bc, int argc, char **argv) +static void handle_options(struct client_config *cfg, int argc, char **argv) { + int rc; + while (1) { int option_index = 0, c; static const struct option long_options[] = { { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, { "server-ip", 1, 0, 'i' }, { "server-port", 1, 0, 'p' }, { "client-id", 1, 0, 'c' }, { "client-slot", 1, 0, 'n' }, + { "atr", 1, 0, 'a' }, + { "event-script", 1, 0, 'e' }, +#ifdef USB_SUPPORT + { "usb-vendor", 1, 0, 'V' }, + { "usb-product", 1, 0, 'P' }, + { "usb-config", 1, 0, 'C' }, + { "usb-interface", 1, 0, 'I' }, + { "usb-altsetting", 1, 0, 'S' }, + { "usb-address", 1, 0, 'A' }, + { "usb-path", 1, 0, 'H' }, +#endif { 0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "hi:p:c:n:", + c = getopt_long(argc, argv, "hvi:p:c:n:e:" +#ifdef USB_SUPPORT + "V:P:C:I:S:A:H:" +#endif + , long_options, &option_index); if (c == -1) break; @@ -53,18 +82,55 @@ static void handle_options(struct bankd_client *bc, int argc, char **argv) printf_help(); exit(0); break; + case 'v': + printf("osmo-remsim-client version %s\n", VERSION); + exit(0); + break; case 'i': - bc->srv_conn.server_host = optarg; + osmo_talloc_replace_string(cfg, &cfg->server_host, optarg); break; case 'p': - bc->srv_conn.server_port = atoi(optarg); + cfg->server_port = atoi(optarg); break; case 'c': - remsim_client_set_clslot(bc, atoi(optarg), -1); + cfg->client_id = atoi(optarg); break; case 'n': - remsim_client_set_clslot(bc, -1, atoi(optarg)); + cfg->client_slot = atoi(optarg); break; + case 'a': + rc = osmo_hexparse(optarg, cfg->atr.data, ARRAY_SIZE(cfg->atr.data)); + if (rc < 2 || rc > ARRAY_SIZE(cfg->atr.data)) { + fprintf(stderr, "ATR malformed\n"); + exit(2); + } + break; + case 'e': + osmo_talloc_replace_string(cfg, &cfg->event_script, optarg); + break; +#ifdef USB_SUPPORT + case 'V': + cfg->usb.vendor_id = strtol(optarg, NULL, 16); + break; + case 'P': + cfg->usb.product_id = strtol(optarg, NULL, 16); + break; + case 'C': + cfg->usb.config_id = atoi(optarg); + break; + case 'I': + cfg->usb.if_num = atoi(optarg); + break; + case 'S': + cfg->usb.altsetting = atoi(optarg); + break; + case 'A': + cfg->usb.addr = atoi(optarg); + break; + case 'H': + cfg->usb.path = optarg; + break; +#endif default: break; } @@ -74,6 +140,7 @@ static void handle_options(struct bankd_client *bc, int argc, char **argv) int main(int argc, char **argv) { struct bankd_client *g_client; + struct client_config *cfg; char hostname[256]; gethostname(hostname, sizeof(hostname)); @@ -84,9 +151,11 @@ int main(int argc, char **argv) osmo_init_logging2(g_tall_ctx, &log_info); - g_client = remsim_client_create(g_tall_ctx, hostname, "remsim-client"); + cfg = client_config_init(g_tall_ctx); + OSMO_ASSERT(cfg); + handle_options(cfg, argc, argv); - handle_options(g_client, argc, argv); + g_client = remsim_client_create(g_tall_ctx, hostname, "remsim-client",cfg); osmo_fsm_inst_dispatch(g_client->srv_conn.fi, SRVC_E_ESTABLISH, NULL); diff --git a/src/client/simtrace2-remsim_client.c b/src/client/simtrace2-remsim_client.c deleted file mode 100644 index e495f10..0000000 --- a/src/client/simtrace2-remsim_client.c +++ /dev/null @@ -1,1284 +0,0 @@ -/* (C) 2018-2020 by Harald Welte - * (C) 2018 by sysmocom - s.f.m.c. GmbH, Author: Kevin Redon - * - * All Rights Reserved - * - * SPDX-License-Identifier: GPL-2.0+ - * - * 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 -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "rspro_util.h" -#include "client.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* transport to a SIMtrace device */ -struct st_transport { - /* USB */ - struct libusb_device_handle *usb_devh; - struct { - uint8_t in; - uint8_t out; - uint8_t irq_in; - } usb_ep; -}; - -/* a SIMtrace slot; communicates over a transport */ -struct st_slot { - /* transport through which the slot can be reached */ - struct st_transport *transp; - /* number of the slot within the transport */ - uint8_t slot_nr; -}; - -/* One istance of card emulation */ -struct cardem_inst { - /* slot on which this card emulation instance runs */ - struct st_slot *slot; - struct cardemu_usb_msg_status last_status; - char *usb_path; -}; - -/* global GSMTAP instance */ -static struct gsmtap_inst *g_gti; - -struct bankd_client *g_client; -static void *g_tall_ctx; -void __thread *talloc_asn1_ctx; -int asn_debug; - -/* should we leave main loop processing? */ -bool g_leave_main = false; - -__attribute__((unused)) static int gsmtap_send_sim(const uint8_t *apdu, unsigned int len) -{ - struct gsmtap_hdr *gh; - unsigned int gross_len = len + sizeof(*gh); - uint8_t *buf = malloc(gross_len); - int rc; - - if (!buf) - return -ENOMEM; - - memset(buf, 0, sizeof(*gh)); - gh = (struct gsmtap_hdr *) buf; - gh->version = GSMTAP_VERSION; - gh->hdr_len = sizeof(*gh)/4; - gh->type = GSMTAP_TYPE_SIM; - - memcpy(buf + sizeof(*gh), apdu, len); - - rc = write(gsmtap_inst_fd(g_gti), buf, gross_len); - if (rc < 0) { - perror("write gsmtap"); - free(buf); - return rc; - } - - free(buf); - return 0; -} - -/* build the (additional) environment for executing a script */ -static char **build_script_env(struct bankd_client *clnt, const char *cause) -{ - struct cardem_inst *ci = clnt->cardem; - char **env = talloc_zero_size(clnt, 256*sizeof(char *)); - int i = 0; - - if (!env) - return NULL; - - env[i++] = talloc_asprintf(env, "REMSIM_CLIENT_VERSION=%s", VERSION); - - env[i++] = talloc_asprintf(env, "REMSIM_SERVER_ADDR=%s:%u", - clnt->srv_conn.server_host, clnt->srv_conn.server_port); - env[i++] = talloc_asprintf(env, "REMSIM_SERVER_STATE=%s", - osmo_fsm_inst_state_name(clnt->srv_conn.fi)); - - env[i++] = talloc_asprintf(env, "REMSIM_BANKD_ADDR=%s:%u", - clnt->bankd_conn.server_host, clnt->bankd_conn.server_port); - env[i++] = talloc_asprintf(env, "REMSIM_BANKD_STATE=%s", - osmo_fsm_inst_state_name(clnt->bankd_conn.fi)); - - - if (clnt->srv_conn.clslot) { - env[i++] = talloc_asprintf(env, "REMSIM_CLIENT_SLOT=%lu:%lu", - g_client->srv_conn.clslot->clientId, - g_client->srv_conn.clslot->slotNr); - } - env[i++] = talloc_asprintf(env, "REMSIM_BANKD_SLOT=%u:%u", - clnt->bankd_slot.bank_id, clnt->bankd_slot.slot_nr); - - env[i++] = talloc_asprintf(env, "REMSIM_USB_PATH=%s", ci->usb_path); - env[i++] = talloc_asprintf(env, "REMSIM_USB_INTERFACE=%u", clnt->cfg->usb.if_num); - - /* TODO: SIM card state VCC/CLK/RST */ - env[i++] = talloc_asprintf(env, "REMSIM_SIM_VCC=%u", - !!(ci->last_status.flags & CEMU_STATUS_F_VCC_PRESENT)); - env[i++] = talloc_asprintf(env, "REMSIM_SIM_RST=%u", - !!(ci->last_status.flags & CEMU_STATUS_F_RESET_ACTIVE)); - - env[i++] = talloc_asprintf(env, "REMSIM_CAUSE=%s", cause); - - /* terminate last entry */ - env[i++] = NULL; - return env; -} - -static int call_script(struct bankd_client *clnt, const char *cause) -{ - char **env, *cmd; - int rc; - - if (!clnt->cfg->event_script) - return 0; - - env = build_script_env(clnt, cause); - if (!env) - return -ENOMEM; - - cmd = talloc_asprintf(env, "%s %s", clnt->cfg->event_script, cause); - if (!cmd) { - talloc_free(env); - return -ENOMEM; - } - - rc = osmo_system_nowait(cmd, osmo_environment_whitelist, env); - talloc_free(env); - - return rc; -} - -/*********************************************************************** - * SIMTRACE core protocol - ***********************************************************************/ - -/*! \brief allocate a message buffer for simtrace use */ -static struct msgb *st_msgb_alloc(void) -{ - return msgb_alloc_headroom(1024+32, 32, "SIMtrace"); -} - -#if 0 -static void apdu_out_cb(uint8_t *buf, unsigned int len, void *user_data) -{ - printf("APDU: %s\n", osmo_hexdump(buf, len)); - gsmtap_send_sim(buf, len); -} -#endif - -static void usb_out_xfer_cb(struct libusb_transfer *xfer) -{ - struct msgb *msg = xfer->user_data; - - switch (xfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - break; - case LIBUSB_TRANSFER_NO_DEVICE: - fprintf(stderr, "USB device disappeared\n"); - g_leave_main = true; - break; - default: - fprintf(stderr, "USB OUT transfer failed, status=%u\n", xfer->status); - g_leave_main = true; - break; - } - - msgb_free(msg); - libusb_free_transfer(xfer); -} - -/*! \brief Transmit a given command to the SIMtrace2 device */ -int st_transp_tx_msg(struct st_transport *transp, struct msgb *msg) -{ - struct libusb_transfer *xfer; - int rc; - - printf("SIMtrace <- %s\n", msgb_hexdump(msg)); - - xfer = libusb_alloc_transfer(0); - OSMO_ASSERT(xfer); - xfer->dev_handle = transp->usb_devh; - xfer->flags = 0; - xfer->type = LIBUSB_TRANSFER_TYPE_BULK; - xfer->endpoint = transp->usb_ep.out; - xfer->timeout = 1000; - xfer->user_data = msg; - xfer->length = msgb_length(msg); - xfer->buffer = msgb_data(msg); - xfer->callback = usb_out_xfer_cb; - - /* submit the OUT transfer */ - rc = libusb_submit_transfer(xfer); - OSMO_ASSERT(rc == 0); - - return rc; -} - -static struct simtrace_msg_hdr *st_push_hdr(struct msgb *msg, uint8_t msg_class, uint8_t msg_type, - uint8_t slot_nr) -{ - struct simtrace_msg_hdr *sh; - - sh = (struct simtrace_msg_hdr *) msgb_push(msg, sizeof(*sh)); - memset(sh, 0, sizeof(*sh)); - sh->msg_class = msg_class; - sh->msg_type = msg_type; - sh->slot_nr = slot_nr; - sh->msg_len = msgb_length(msg); - - return sh; -} - -/* transmit a given message to a specified slot. Expects all headers - * present before calling the function */ -int st_slot_tx_msg(struct st_slot *slot, struct msgb *msg, - uint8_t msg_class, uint8_t msg_type) -{ - st_push_hdr(msg, msg_class, msg_type, slot->slot_nr); - - return st_transp_tx_msg(slot->transp, msg); -} - -/*********************************************************************** - * Card Emulation protocol - ***********************************************************************/ - - -/*! \brief Request the SIMtrace2 to generate a card-insert signal */ -static int cardem_request_card_insert(struct cardem_inst *ci, bool inserted) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_cardinsert *cins; - - cins = (struct cardemu_usb_msg_cardinsert *) msgb_put(msg, sizeof(*cins)); - memset(cins, 0, sizeof(*cins)); - if (inserted) - cins->card_insert = 1; - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_CARDINSERT); -} - -/*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Rx */ -static int cardem_request_pb_and_rx(struct cardem_inst *ci, uint8_t pb, uint8_t le) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_tx_data *txd; - txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd)); - - printf("SIMtrace <= %s(%02x, %d)\n", __func__, pb, le); - - memset(txd, 0, sizeof(*txd)); - txd->data_len = 1; - txd->flags = CEMU_DATA_F_PB_AND_RX; - /* one data byte */ - msgb_put_u8(msg, pb); - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA); -} - -/*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Tx */ -static int cardem_request_pb_and_tx(struct cardem_inst *ci, uint8_t pb, - const uint8_t *data, uint16_t data_len_in) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_tx_data *txd; - uint8_t *cur; - - txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd)); - - printf("SIMtrace <= %s(%02x, %s, %d)\n", __func__, pb, - osmo_hexdump(data, data_len_in), data_len_in); - - memset(txd, 0, sizeof(*txd)); - txd->data_len = 1 + data_len_in; - txd->flags = CEMU_DATA_F_PB_AND_TX; - /* procedure byte */ - msgb_put_u8(msg, pb); - /* data */ - cur = msgb_put(msg, data_len_in); - memcpy(cur, data, data_len_in); - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA); -} - -/*! \brief Request the SIMtrace2 to send a Status Word */ -static int cardem_request_sw_tx(struct cardem_inst *ci, const uint8_t *sw) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_tx_data *txd; - uint8_t *cur; - - txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd)); - - printf("SIMtrace <= %s(%02x %02x)\n", __func__, sw[0], sw[1]); - - memset(txd, 0, sizeof(*txd)); - txd->data_len = 2; - txd->flags = CEMU_DATA_F_PB_AND_TX | CEMU_DATA_F_FINAL; - cur = msgb_put(msg, 2); - cur[0] = sw[0]; - cur[1] = sw[1]; - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA); -} - -/*! \brief Request the SIMtrace2 to send a Status Word */ -static int cardem_request_config(struct cardem_inst *ci, uint32_t features) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_config *cfg; - - cfg = (struct cardemu_usb_msg_config *) msgb_put(msg, sizeof(*cfg)); - - printf("SIMtrace <= %s(%08x)\n", __func__, features); - - memset(cfg, 0, sizeof(*cfg)); - cfg->features = features; - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_BD_CEMU_CONFIG); -} - -// FIXME check if the ATR actually includes a checksum -__attribute__((unused)) static void atr_update_csum(uint8_t *atr, unsigned int atr_len) -{ - uint8_t csum = 0; - int i; - - for (i = 1; i < atr_len - 1; i++) - csum = csum ^ atr[i]; - - atr[atr_len-1] = csum; -} - -static int cardem_request_set_atr(struct cardem_inst *ci, const uint8_t *atr, unsigned int atr_len) -{ - struct msgb *msg = st_msgb_alloc(); - struct cardemu_usb_msg_set_atr *satr; - uint8_t *cur; - - satr = (struct cardemu_usb_msg_set_atr *) msgb_put(msg, sizeof(*satr)); - - printf("SIMtrace <= %s(%s)\n", __func__, osmo_hexdump(atr, atr_len)); - - memset(satr, 0, sizeof(*satr)); - satr->atr_len = atr_len; - cur = msgb_put(msg, atr_len); - memcpy(cur, atr, atr_len); - - return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_SET_ATR); -} - -/*********************************************************************** - * Modem Control protocol - ***********************************************************************/ - -static int _modem_reset(struct st_slot *slot, uint8_t asserted, uint16_t pulse_ms) -{ - struct msgb *msg = st_msgb_alloc(); - struct st_modem_reset *sr ; - - sr = (struct st_modem_reset *) msgb_put(msg, sizeof(*sr)); - sr->asserted = asserted; - sr->pulse_duration_msec = pulse_ms; - - return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_DT_MODEM_RESET); -} - -/*! \brief pulse the RESET line of the modem for \a duration_ms milli-seconds*/ -int st_modem_reset_pulse(struct st_slot *slot, uint16_t duration_ms) -{ - return _modem_reset(slot, 2, duration_ms); -} - -/*! \brief assert the RESET line of the modem */ -int st_modem_reset_active(struct st_slot *slot) -{ - return _modem_reset(slot, 1, 0); -} - -/*! \brief de-assert the RESET line of the modem */ -int st_modem_reset_inactive(struct st_slot *slot) -{ - return _modem_reset(slot, 0, 0); -} - -static int _modem_sim_select(struct st_slot *slot, uint8_t remote_sim) -{ - struct msgb *msg = st_msgb_alloc(); - struct st_modem_sim_select *ss; - - ss = (struct st_modem_sim_select *) msgb_put(msg, sizeof(*ss)); - ss->remote_sim = remote_sim; - - return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_DT_MODEM_SIM_SELECT); -} - -/*! \brief select local (physical) SIM for given slot */ -int st_modem_sim_select_local(struct st_slot *slot) -{ - return _modem_sim_select(slot, 0); -} - -/*! \brief select remote (emulated/forwarded) SIM for given slot */ -int st_modem_sim_select_remote(struct st_slot *slot) -{ - return _modem_sim_select(slot, 1); -} - -/*! \brief Request slot to send us status information about the modem */ -int st_modem_get_status(struct st_slot *slot) -{ - struct msgb *msg = st_msgb_alloc(); - - return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_BD_MODEM_STATUS); -} - - -/*********************************************************************** - * Incoming Messages - ***********************************************************************/ - -/*! \brief Process a STATUS message from the SIMtrace2 */ -static int process_do_status(struct cardem_inst *ci, uint8_t *buf, int len) -{ - struct cardemu_usb_msg_status *status; - status = (struct cardemu_usb_msg_status *) buf; - - printf("SIMtrace => STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n", - status->flags, status->fi, status->di, status->wi, - status->waiting_time); - - return 0; -} - -/*! \brief Process a PTS indication message from the SIMtrace2 */ -static int process_do_pts(struct cardem_inst *ci, uint8_t *buf, int len) -{ - struct cardemu_usb_msg_pts_info *pts; - pts = (struct cardemu_usb_msg_pts_info *) buf; - - printf("SIMtrace => PTS req: %s\n", osmo_hexdump(pts->req, sizeof(pts->req))); - - return 0; -} - -/*! \brief Process a ERROR indication message from the SIMtrace2 */ -__attribute__((unused)) static int process_do_error(struct cardem_inst *ci, uint8_t *buf, int len) -{ - struct cardemu_usb_msg_error *err; - err = (struct cardemu_usb_msg_error *) buf; - - printf("SIMtrace => ERROR: %u/%u/%u: %s\n", - err->severity, err->subsystem, err->code, - err->msg_len ? (char *)err->msg : ""); - - return 0; -} - -static struct osmo_apdu_context ac; // this will hold the complete APDU (across calls) - -/*! \brief Process a RX-DATA indication message from the SIMtrace2 */ -static int process_do_rx_da(struct cardem_inst *ci, uint8_t *buf, int len) -{ - struct cardemu_usb_msg_rx_data *data = (struct cardemu_usb_msg_rx_data *) buf; // cast the data from the USB message - int rc; - - printf("SIMtrace => DATA: flags=%x, %s: ", data->flags, - osmo_hexdump(data->data, data->data_len)); - - rc = osmo_apdu_segment_in(&ac, data->data, data->data_len, - data->flags & CEMU_DATA_F_TPDU_HDR); // parse the APDU data in the USB message - - if (rc & APDU_ACT_TX_CAPDU_TO_CARD) { // there is no pending data coming from the modem - uint8_t apdu_command[sizeof(ac.hdr) + ac.lc.tot]; // to store the APDU command to send - memcpy(apdu_command, &ac.hdr, sizeof(ac.hdr)); // copy APDU command header - if (ac.lc.tot) { - memcpy(apdu_command + sizeof(ac.hdr), ac.dc, ac.lc.tot); // copy APDU command data - } - // send APDU to card - BankSlot_t bslot; - bank_slot2rspro(&bslot, &g_client->bankd_slot); - RsproPDU_t *pdu = rspro_gen_TpduModem2Card(g_client->srv_conn.clslot, &bslot, apdu_command, sizeof(ac.hdr) + ac.lc.tot); // create RSPRO packet - server_conn_send_rspro(&g_client->bankd_conn, pdu); - // the response will come separately - } else if (ac.lc.tot > ac.lc.cur) { // there is pending data from the modem - cardem_request_pb_and_rx(ci, ac.hdr.ins, ac.lc.tot - ac.lc.cur); // send procedure byte to get remaining data - } - return 0; -} - -#if 0 - case SIMTRACE_CMD_DO_ERROR - rc = process_do_error(ci, buf, len); - break; -#endif - -/*! \brief Process an incoming message from the SIMtrace2 */ -static int process_usb_msg(struct cardem_inst *ci, uint8_t *buf, int len) -{ - struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf; - int rc; - - printf("SIMtrace -> %s\n", osmo_hexdump(buf, len)); - - buf += sizeof(*sh); - - switch (sh->msg_type) { - case SIMTRACE_MSGT_BD_CEMU_STATUS: - rc = process_do_status(ci, buf, len); - break; - case SIMTRACE_MSGT_DO_CEMU_PTS: - rc = process_do_pts(ci, buf, len); - break; - case SIMTRACE_MSGT_DO_CEMU_RX_DATA: - rc = process_do_rx_da(ci, buf, len); - break; - case SIMTRACE_MSGT_BD_CEMU_CONFIG: - /* firmware confirms configuration change; ignore */ - break; - default: - printf("unknown simtrace msg type 0x%02x\n", sh->msg_type); - rc = -1; - break; - } - - return rc; -} - - -/*! \brief Process a STATUS message on IRQ endpoint from the SIMtrace2 */ -static int process_irq_status(struct cardem_inst *ci, const uint8_t *buf, int len) -{ - const struct cardemu_usb_msg_status *status = (struct cardemu_usb_msg_status *) buf; - - printf("SIMtrace IRQ STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n", - status->flags, status->fi, status->di, status->wi, - status->waiting_time); - - BankSlot_t bslot; - bank_slot2rspro(&bslot, &g_client->bankd_slot); - RsproPDU_t *pdu = rspro_gen_ClientSlotStatusInd(g_client->srv_conn.clslot, &bslot, - status->flags & CEMU_STATUS_F_RESET_ACTIVE, - status->flags & CEMU_STATUS_F_VCC_PRESENT, - status->flags & CEMU_STATUS_F_CLK_ACTIVE, - -1 /* FIXME: make this dependent on board */); - server_conn_send_rspro(&g_client->bankd_conn, pdu); - - if (ci->last_status.flags != status->flags) { - ci->last_status = *status; - call_script(g_client, "event-modem-status"); - } else - ci->last_status = *status; - - return 0; -} - -static int process_usb_msg_irq(struct cardem_inst *ci, const uint8_t *buf, unsigned int len) -{ - struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf; - int rc; - - printf("SIMtrace IRQ %s\n", osmo_hexdump(buf, len)); - - buf += sizeof(*sh); - - switch (sh->msg_type) { - case SIMTRACE_MSGT_BD_CEMU_STATUS: - rc = process_irq_status(ci, buf, len); - break; - default: - printf("unknown simtrace msg type 0x%02x\n", sh->msg_type); - rc = -1; - break; - } - - return rc; -} - -static void usb_in_xfer_cb(struct libusb_transfer *xfer) -{ - struct cardem_inst *ci = xfer->user_data; - int rc; - - switch (xfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - /* hand the message up the stack */ - process_usb_msg(ci, xfer->buffer, xfer->actual_length); - break; - case LIBUSB_TRANSFER_NO_DEVICE: - fprintf(stderr, "USB device disappeared\n"); - g_leave_main = true; - break; - default: - fprintf(stderr, "USB IN transfer failed, status=%u\n", xfer->status); - g_leave_main = true; - break; - } - - /* re-submit the IN transfer */ - rc = libusb_submit_transfer(xfer); - OSMO_ASSERT(rc == 0); -} - - -static void allocate_and_submit_in(struct cardem_inst *ci) -{ - struct st_transport *transp = ci->slot->transp; - struct libusb_transfer *xfer; - int rc; - - xfer = libusb_alloc_transfer(0); - OSMO_ASSERT(xfer); - xfer->dev_handle = transp->usb_devh; - xfer->flags = 0; - xfer->type = LIBUSB_TRANSFER_TYPE_BULK; - xfer->endpoint = transp->usb_ep.in; - xfer->timeout = 0; - xfer->user_data = ci; - xfer->length = 16*256; - - xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length); - OSMO_ASSERT(xfer->buffer); - xfer->callback = usb_in_xfer_cb; - - /* submit the IN transfer */ - rc = libusb_submit_transfer(xfer); - OSMO_ASSERT(rc == 0); -} - - -static void usb_irq_xfer_cb(struct libusb_transfer *xfer) -{ - struct cardem_inst *ci = xfer->user_data; - int rc; - - switch (xfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - process_usb_msg_irq(ci, xfer->buffer, xfer->actual_length); - break; - case LIBUSB_TRANSFER_NO_DEVICE: - fprintf(stderr, "USB device disappeared\n"); - g_leave_main = true; - break; - default: - fprintf(stderr, "USB IRQ transfer failed, status=%u\n", xfer->status); - g_leave_main = true; - break; - } - - /* re-submit the IN transfer */ - rc = libusb_submit_transfer(xfer); - OSMO_ASSERT(rc == 0); -} - - -static void allocate_and_submit_irq(struct cardem_inst *ci) -{ - struct st_transport *transp = ci->slot->transp; - struct libusb_transfer *xfer; - int rc; - - xfer = libusb_alloc_transfer(0); - OSMO_ASSERT(xfer); - xfer->dev_handle = transp->usb_devh; - xfer->flags = 0; - xfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; - xfer->endpoint = transp->usb_ep.irq_in; - xfer->timeout = 0; - xfer->user_data = ci; - xfer->length = 64; - - xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length); - OSMO_ASSERT(xfer->buffer); - xfer->callback = usb_irq_xfer_cb; - - /* submit the IN transfer */ - rc = libusb_submit_transfer(xfer); - OSMO_ASSERT(rc == 0); -} - - -static struct st_transport _transp; - -static struct st_slot _slot = { - .transp = &_transp, - .slot_nr = 0, -}; - -static struct cardem_inst *g_ci; - -static void signal_handler(int signal) -{ - switch (signal) { - case SIGINT: - cardem_request_card_insert(g_ci, false); - exit(0); - break; - default: - break; - } -} - -/** remsim_client **/ - -static int bankd_handle_tpduCardToModem(struct bankd_client *bc, const RsproPDU_t *pdu) -{ - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_tpduCardToModem == pdu->msg.present); - - const struct TpduCardToModem *card2modem = &pdu->msg.choice.tpduCardToModem; - if (card2modem->data.size < 2) { // at least the two SW bytes are needed - return -1; - } - - // save SW to our current APDU context - ac.sw[0] = card2modem->data.buf[card2modem->data.size - 2]; - ac.sw[1] = card2modem->data.buf[card2modem->data.size - 1]; - printf("SIMtrace <= SW=0x%02x%02x, len_rx=%d\n", ac.sw[0], ac.sw[1], card2modem->data.size - 2); - if (card2modem->data.size > 2) { // send PB and data to modem - cardem_request_pb_and_tx(bc->cardem, ac.hdr.ins, card2modem->data.buf, card2modem->data.size - 2); - } - cardem_request_sw_tx(bc->cardem, ac.sw); // send SW to modem - - return 0; -} - -static int bankd_handle_setAtrReq(struct bankd_client *bc, const RsproPDU_t *pdu) -{ - RsproPDU_t *resp; - int rc; - - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_setAtrReq == pdu->msg.present); - - /* FIXME: is this permitted at any time by the SIMtrace2 cardemfirmware? */ - rc = cardem_request_set_atr(bc->cardem, pdu->msg.choice.setAtrReq.atr.buf, - pdu->msg.choice.setAtrReq.atr.size); - if (rc == 0) - resp = rspro_gen_SetAtrRes(ResultCode_ok); - else - resp = rspro_gen_SetAtrRes(ResultCode_cardTransmissionError); - if (!resp) - return -ENOMEM; - server_conn_send_rspro(&g_client->bankd_conn, resp); - - return 0; -} - -/* handle incoming message from bankd */ -static int bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu) -{ - switch (pdu->msg.present) { - case RsproPDUchoice_PR_connectClientRes: - /* Store 'identity' of bankd to in peer_comp_id */ - rspro_comp_id_retrieve(&bankdc->peer_comp_id, &pdu->msg.choice.connectClientRes.identity); - osmo_fsm_inst_dispatch(bankdc->fi, SRVC_E_CLIENT_CONN_RES, (void *) pdu); - call_script(g_client, "event-bankd-connect"); - break; - case RsproPDUchoice_PR_tpduCardToModem: // APDU response from card received - bankd_handle_tpduCardToModem(g_client, pdu); - break; - case RsproPDUchoice_PR_setAtrReq: - bankd_handle_setAtrReq(g_client, pdu); - break; - default: - LOGPFSML(bankdc->fi, LOGL_ERROR, "Unknown/Unsuppoerted RSPRO PDU %s\n", - rspro_msgt_name(pdu)); - return -1; - } - - return 0; -} - -/* handle incoming messages from server */ -static int srvc_handle_rx(struct rspro_server_conn *srvc, const RsproPDU_t *pdu) -{ - RsproPDU_t *resp; - - switch (pdu->msg.present) { - case RsproPDUchoice_PR_connectClientRes: - /* Store 'identity' of server in srvc->peer_comp_id */ - rspro_comp_id_retrieve(&srvc->peer_comp_id, &pdu->msg.choice.connectClientRes.identity); - osmo_fsm_inst_dispatch(srvc->fi, SRVC_E_CLIENT_CONN_RES, (void *) pdu); - call_script(g_client, "event-server-connect"); - break; - case RsproPDUchoice_PR_configClientIdReq: - /* store/set the clientID as instructed by the server */ - if (!g_client->srv_conn.clslot) - g_client->srv_conn.clslot = talloc_zero(g_client, ClientSlot_t); - *g_client->srv_conn.clslot = pdu->msg.choice.configClientIdReq.clientSlot; - if (!g_client->bankd_conn.clslot) - g_client->bankd_conn.clslot = talloc_zero(g_client, ClientSlot_t); - *g_client->bankd_conn.clslot = *g_client->srv_conn.clslot; - /* send response to server */ - resp = rspro_gen_ConfigClientIdRes(ResultCode_ok); - server_conn_send_rspro(srvc, resp); - break; - case RsproPDUchoice_PR_configClientBankReq: - /* store/set the bankd ip/port as instructed by the server */ - osmo_talloc_replace_string(g_client, &g_client->bankd_conn.server_host, - rspro_IpAddr2str(&pdu->msg.choice.configClientBankReq.bankd.ip)); - rspro2bank_slot(&g_client->bankd_slot, &pdu->msg.choice.configClientBankReq.bankSlot); - g_client->bankd_conn.server_port = pdu->msg.choice.configClientBankReq.bankd.port; - /* bankd port 0 is a magic value to indicate "no bankd" */ - if (g_client->bankd_conn.server_port == 0) - osmo_fsm_inst_dispatch(g_client->bankd_conn.fi, SRVC_E_DISCONNECT, NULL); - else - osmo_fsm_inst_dispatch(g_client->bankd_conn.fi, SRVC_E_ESTABLISH, NULL); - /* send response to server */ - resp = rspro_gen_ConfigClientBankRes(ResultCode_ok); - server_conn_send_rspro(srvc, resp); - call_script(g_client, "event-config-bankd"); - break; - default: - LOGPFSML(srvc->fi, LOGL_ERROR, "Unknown/Unsupported RSPRO PDU type: %s\n", - rspro_msgt_name(pdu)); - return -1; - } - - return 0; -} - -static void handle_sig_usr1(int signal) -{ - OSMO_ASSERT(signal == SIGUSR1); - talloc_report_full(g_tall_ctx, stderr); - printf("===== NULL\n"); - talloc_report_full(NULL, stderr); -} - -static void print_welcome(void) -{ - printf("simtrace2-remsim-client - Remote SIM card client for SIMtrace\n" - "(C) 2010-2020, Harald Welte \n" - "(C) 2018, sysmocom -s.f.m.c. GmbH, Author: Kevin Redon \n\n"); -} - -static void print_help(void) -{ - printf( "\t-s\t--server-host HOST\n" - "\t-p\t--server-port PORT\n" - "\t-c\t--client-id <0-65535>\n" - "\t-n\t--client-slot <0-65535>\n" - "\t-h\t--help\n" - "\t-v\t--version\n" - "\t-i\t--gsmtap-ip\tA.B.C.D\n" - "\t-k\t--keep-running\n" - "\t-V\t--usb-vendor\tVENDOR_ID\n" - "\t-P\t--usb-product\tPRODUCT_ID\n" - "\t-C\t--usb-config\tCONFIG_ID\n" - "\t-I\t--usb-interface\tINTERFACE_ID\n" - "\t-S\t--usb-altsetting ALTSETTING_ID\n" - "\t-A\t--usb-address\tADDRESS\n" - "\t-H\t--usb-path\tPATH\n" - "\t-a\t--atr\tATR\n" - "\t-e\t--event-script\tPATH\n" - "\n" - ); -} - -static struct client_config *client_config_init(void *ctx) -{ - struct client_config *cfg = talloc_zero(ctx, struct client_config); - if (!cfg) - return NULL; - - cfg->server_host = talloc_strdup(cfg, "127.0.0.1"); - cfg->server_port = 9998; - cfg->client_id = -1; - cfg->client_slot = -1; - cfg->gsmtap_host = talloc_strdup(cfg, "127.0.0.1"); - cfg->keep_running = false; - - cfg->usb.vendor_id = -1; - cfg->usb.product_id = -1; - cfg->usb.config_id = -1; - cfg->usb.if_num = -1; - cfg->usb.altsetting = 0; - cfg->usb.addr = -1; - cfg->usb.path = NULL; - - cfg->atr.data[0] = 0x3B; - cfg->atr.data[1] = 0x00; // the shortest simplest ATR possible - cfg->atr.len = 2; - - return cfg; -}; - -static void handle_options(struct client_config *cfg, int argc, char **argv) -{ - const struct option opts[] = { - { "server-host", 1, 0, 's' }, - { "server-port", 1, 0, 'p' }, - { "client-id", 1, 0, 'c' }, - { "client-slot", 1, 0, 'n' }, - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'v' }, - { "gsmtap-ip", 1, 0, 'i' }, - { "keep-running", 0, 0, 'k' }, - { "usb-vendor", 1, 0, 'V' }, - { "usb-product", 1, 0, 'P' }, - { "usb-config", 1, 0, 'C' }, - { "usb-interface", 1, 0, 'I' }, - { "usb-altsetting", 1, 0, 'S' }, - { "usb-address", 1, 0, 'A' }, - { "usb-path", 1, 0, 'H' }, - { "atr", 1, 0, 'a' }, - { "event-script", 1, 0, 'e' }, - { NULL, 0, 0, 0 } - }; - int c, rc; - - while (1) { - int option_index = 0; - - c = getopt_long(argc, argv, "s:p:c:n:hvi:kV:P:C:I:S:A:H:a:e:", opts, &option_index); - if (c == -1) - break; - switch (c) { - case 's': - osmo_talloc_replace_string(cfg, &cfg->server_host, optarg); - break; - case 'p': - cfg->server_port = atoi(optarg); - break; - case 'c': - cfg->client_id = atoi(optarg); - break; - case 'n': - cfg->client_slot = atoi(optarg); - break; - case 'h': - print_help(); - exit(0); - break; - case 'v': - printf("osmo-remsim-client version %s\n", VERSION); - exit(0); - break; - case 'i': - osmo_talloc_replace_string(cfg, &cfg->gsmtap_host, optarg); - break; - case 'k': - cfg->keep_running = 1; - break; - case 'V': - cfg->usb.vendor_id = strtol(optarg, NULL, 16); - break; - case 'P': - cfg->usb.product_id = strtol(optarg, NULL, 16); - break; - case 'C': - cfg->usb.config_id = atoi(optarg); - break; - case 'I': - cfg->usb.if_num = atoi(optarg); - break; - case 'S': - cfg->usb.altsetting = atoi(optarg); - break; - case 'A': - cfg->usb.addr = atoi(optarg); - break; - case 'H': - cfg->usb.path = optarg; - break; - case 'a': - rc = osmo_hexparse(optarg, cfg->atr.data, ARRAY_SIZE(cfg->atr.data)); - if (rc < 2 || rc > ARRAY_SIZE(cfg->atr.data)) { - fprintf(stderr, "ATR malformed\n"); - exit(2); - } - cfg->atr.len = rc; - break; - case 'e': - osmo_talloc_replace_string(cfg, &cfg->event_script, optarg); - break; - } - } - - if (argc > optind) { - fprintf(stderr, "Unsupported positional arguments on command line\n"); - exit(2); - } -} - - -static void main_body(struct cardem_inst *ci, struct client_config *cfg) -{ - struct st_transport *transp = ci->slot->transp; - struct usb_interface_match _ifm, *ifm = &_ifm; - int rc, i; - - ifm->vendor = cfg->usb.vendor_id; - ifm->product = cfg->usb.product_id; - ifm->configuration = cfg->usb.config_id; - ifm->interface = cfg->usb.if_num; - ifm->altsetting = cfg->usb.altsetting; - ifm->addr = cfg->usb.addr; - if (cfg->usb.path) - osmo_strlcpy(ifm->path, cfg->usb.path, sizeof(ifm->path)); - transp->usb_devh = osmo_libusb_open_claim_interface(NULL, NULL, ifm); - if (!transp->usb_devh) { - fprintf(stderr, "can't open USB device\n"); - return; - } - - /* (re)determine the USB path of the opened device */ - talloc_free(ci->usb_path); - ci->usb_path = osmo_libusb_dev_get_path_c(ci, libusb_get_device(transp->usb_devh)); - - rc = libusb_claim_interface(transp->usb_devh, cfg->usb.if_num); - if (rc < 0) { - fprintf(stderr, "can't claim interface %d; rc=%d\n", cfg->usb.if_num, rc); - goto close_exit; - } - - rc = osmo_libusb_get_ep_addrs(transp->usb_devh, cfg->usb.if_num, &transp->usb_ep.out, - &transp->usb_ep.in, &transp->usb_ep.irq_in); - if (rc < 0) { - fprintf(stderr, "can't obtain EP addrs; rc=%d\n", rc); - goto close_exit; - } - - // switch modem SIM port to emulated SIM on OWHW - if (USB_VENDOR_OPENMOKO == ifm->vendor && USB_PRODUCT_OWHW_SAM3 == ifm->product) { // we are on the OWHW - int modem = -1; - switch (ifm->interface) { // the USB interface indicates for which modem we want to emulate the SIM - case 0: - modem = 1; - break; - case 1: - modem = 2; - break; - default: - fprintf(stderr, "unknown GPIO for SIMtrace interface %d\n", ifm->interface); - goto close_exit; - } - // - char gpio_path[PATH_MAX]; - snprintf(gpio_path, sizeof(gpio_path), "/dev/gpio/connect_st_usim%d/value", modem); - int connec_st_usim = open(gpio_path, O_WRONLY); - if (-1 == connec_st_usim) { - fprintf(stderr, "can't open GPIO %s to switch modem %d to emulated USIM\n", gpio_path, modem); - goto close_exit; - } - if (1 != write(connec_st_usim, "1", 1)) { - fprintf(stderr, "can't write GPIO %s to switch modem %d to emulated USIM\n", gpio_path, modem); - goto close_exit; - } - printf("switched modem %d to emulated USIM\n", modem); - - snprintf(gpio_path, sizeof(gpio_path), "/dev/gpio/mdm%d_rst/value", modem); - int mdm_rst = open(gpio_path, O_WRONLY); - if (-1 == mdm_rst) { - fprintf(stderr, "can't open GPIO %s to reset modem %d\n", gpio_path, modem); - goto close_exit; - } - if (1 != write(mdm_rst, "1", 1)) { - fprintf(stderr, "can't write GPIO %s to reset modem %d\n", gpio_path, modem); - goto close_exit; - } - sleep(1); // wait a bit to ensure reset is effective - if (1 != write(mdm_rst, "0", 1)) { - fprintf(stderr, "can't write GPIO %s to reset modem %d\n", gpio_path, modem); - goto close_exit; - } - printf("modem %d reset\n", modem); - } - - /* request firmware to generate STATUS on IRQ endpoint */ - cardem_request_config(ci, CEMU_FEAT_F_STATUS_IRQ); - - /* simulate card-insert to modem (owhw, not qmod) */ - cardem_request_card_insert(ci, true); - call_script(g_client, "request-card-insert"); - - /* select remote (forwarded) SIM */ - st_modem_sim_select_remote(ci->slot); - call_script(g_client, "request-sim-remote"); - - /* set the ATR */ - //atr_update_csum(real_atr, sizeof(real_atr)); - cardem_request_set_atr(ci, cfg->atr.data, cfg->atr.len); - - /* select remote (forwarded) SIM */ - st_modem_reset_pulse(ci->slot, 300); - call_script(g_client, "request-modem-reset"); - - printf("Entering main loop\n"); - - allocate_and_submit_irq(ci); - /* submit multiple IN URB in order to work around OS#4409 */ - for (i = 0; i < 4; i++) - allocate_and_submit_in(ci); - - while (!g_leave_main) { - osmo_select_main(false); - } - g_leave_main = false; - - libusb_release_interface(transp->usb_devh, 0); - -close_exit: - if (transp->usb_devh) - libusb_close(transp-> usb_devh); -} - -int main(int argc, char **argv) -{ - struct rspro_server_conn *srvc, *bankdc; - struct client_config *cfg; - int rc; - int ret = 1; - - print_welcome(); - - talloc_enable_null_tracking(); - g_tall_ctx = talloc_named_const(NULL, 0, "global"); - talloc_asn1_ctx = talloc_named_const(g_tall_ctx, 0, "asn1"); - msgb_talloc_ctx_init(g_tall_ctx, 0); - osmo_init_logging2(g_tall_ctx, &log_info); - - g_ci = talloc_zero(g_tall_ctx, struct cardem_inst); - g_ci->slot = &_slot; - - cfg = client_config_init(g_ci); - handle_options(cfg, argc, argv); - - if (cfg->usb.vendor_id < 0 || cfg->usb.product_id < 0) { - fprintf(stderr, "You have to specify the vendor and product ID\n"); - goto do_exit; - } - - signal(SIGUSR1, handle_sig_usr1); - - rc = osmo_libusb_init(NULL); - if (rc < 0) { - fprintf(stderr, "libusb initialization failed\n"); - goto do_exit; - } - - g_gti = gsmtap_source_init(cfg->gsmtap_host, GSMTAP_UDP_PORT, 0); - if (!g_gti) { - perror("unable to open GSMTAP"); - goto close_exit; - } - gsmtap_source_add_sink(g_gti); - - signal(SIGINT, &signal_handler); - - // initialize remote SIM client - - g_client = talloc_zero(g_tall_ctx, struct bankd_client); - g_client->cfg = cfg; - g_client->cardem = g_ci; - - if (cfg->client_id != -1) { - g_client->srv_conn.clslot = talloc_zero(g_client, ClientSlot_t); - g_client->srv_conn.clslot->clientId = cfg->client_id; - /* default to client slot 0 */ - if (cfg->client_slot == -1) - g_client->srv_conn.clslot->slotNr = 0; - else - g_client->srv_conn.clslot->slotNr = cfg->client_slot; - g_client->bankd_conn.clslot = talloc_zero(g_client, ClientSlot_t); - *g_client->bankd_conn.clslot = *g_client->srv_conn.clslot; - } - - /* create and [attempt to] establish connection to remsim-server */ - srvc = &g_client->srv_conn; - srvc->server_host = cfg->server_host; - srvc->server_port = cfg->server_port; - srvc->handle_rx = srvc_handle_rx; - srvc->own_comp_id.type = ComponentType_remsimClient; - OSMO_STRLCPY_ARRAY(srvc->own_comp_id.name, "simtrace2-remsim-client"); - OSMO_STRLCPY_ARRAY(srvc->own_comp_id.software, "remsim-client"); - OSMO_STRLCPY_ARRAY(srvc->own_comp_id.sw_version, PACKAGE_VERSION); - rc = server_conn_fsm_alloc(g_client, srvc); - if (rc < 0) { - fprintf(stderr, "Unable to create Server conn FSM: %s\n", strerror(errno)); - exit(1); - } - osmo_fsm_inst_dispatch(srvc->fi, SRVC_E_ESTABLISH, NULL); - - /* create and not yet establish connection to remsim-bankd */ - srvc = &g_client->srv_conn; - bankdc = &g_client->bankd_conn; - /* server_host / server_port are configured from remsim-server */ - bankdc->handle_rx = bankd_handle_rx; - memcpy(&bankdc->own_comp_id, &srvc->own_comp_id, sizeof(bankdc->own_comp_id)); - rc = server_conn_fsm_alloc(g_client, bankdc); - if (rc < 0) { - fprintf(stderr, "Unable to create bankd conn FSM: %s\n", strerror(errno)); - exit(1); - } - osmo_fsm_inst_update_id(bankdc->fi, "bankd"); - - asn_debug = 0; - - // connect to SIMtrace2 cardem - do { - main_body(g_ci, cfg); - sleep(1); - } while (cfg->keep_running); - -close_exit: - osmo_libusb_exit(NULL); -do_exit: - return ret; -} diff --git a/src/client/user_ifdhandler.c b/src/client/user_ifdhandler.c index 677b781..188d3a6 100644 --- a/src/client/user_ifdhandler.c +++ b/src/client/user_ifdhandler.c @@ -148,70 +148,66 @@ static void enqueue_to_ifd(struct client_thread *ct, struct msgb *msg) } /*********************************************************************** - * Incoming RSPRO messages from bank-daemon (SIM card) + * frontend to remsim-client main FSM code ***********************************************************************/ -static int bankd_handle_tpduCardToModem(struct bankd_client *bc, const RsproPDU_t *pdu) +int frontend_request_card_insert(struct bankd_client *bc) +{ + return 0; +} + +int frontend_request_sim_remote(struct bankd_client *bc) +{ + return 0; +} + +int frontend_request_modem_reset(struct bankd_client *bc) +{ + return 0; +} + +int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len) { - const struct TpduCardToModem *card2modem; struct client_thread *ct = bc->data; struct msgb *msg; - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_tpduCardToModem == pdu->msg.present); + OSMO_ASSERT(data); - card2modem = &pdu->msg.choice.tpduCardToModem; - DEBUGP(DMAIN, "R-APDU: %s\n", osmo_hexdump(card2modem->data.buf, card2modem->data.size)); + DEBUGP(DMAIN, "R-APDU: %s\n", osmo_hexdump(data, len)); /* enqueue towards IFD thread */ - msg = itmsg_alloc(ITMSG_TYPE_R_APDU_IND, 0, card2modem->data.buf, card2modem->data.size); + msg = itmsg_alloc(ITMSG_TYPE_R_APDU_IND, 0, data, len); OSMO_ASSERT(msg); enqueue_to_ifd(ct, msg); return 0; } -static int bankd_handle_setAtrReq(struct bankd_client *bc, const RsproPDU_t *pdu) +int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len) { struct client_thread *ct = bc->data; - RsproPDU_t *resp; unsigned int atr_len; - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_setAtrReq == pdu->msg.present); + OSMO_ASSERT(data); - DEBUGP(DMAIN, "SET_ATR: %s\n", osmo_hexdump(pdu->msg.choice.setAtrReq.atr.buf, - pdu->msg.choice.setAtrReq.atr.size)); + DEBUGP(DMAIN, "SET_ATR: %s\n", osmo_hexdump(data, len)); /* store ATR in local data structure until somebody needs it */ - atr_len = pdu->msg.choice.setAtrReq.atr.size; + atr_len = len; if (atr_len > sizeof(ct->atr)) atr_len = sizeof(ct->atr); - memcpy(ct->atr, pdu->msg.choice.setAtrReq.atr.buf, atr_len); + memcpy(ct->atr, data, atr_len); ct->atr_len = atr_len; - resp = rspro_gen_SetAtrRes(ResultCode_ok); - if (!resp) - return -ENOMEM; - server_conn_send_rspro(&bc->bankd_conn, resp); - return 0; } - -int client_user_bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu) +int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts) { - struct bankd_client *bc = bankdc2bankd_client(bankdc); + return 0; +} - switch (pdu->msg.present) { - case RsproPDUchoice_PR_tpduCardToModem: - bankd_handle_tpduCardToModem(bc, pdu); - break; - case RsproPDUchoice_PR_setAtrReq: - bankd_handle_setAtrReq(bc, pdu); - break; - default: - OSMO_ASSERT(0); - } +int frontend_append_script_env(struct bankd_client *bc, char **env, size_t max_env) +{ return 0; } @@ -363,6 +359,7 @@ static void client_pthread_cleanup(void *arg) static void *client_pthread_main(void *arg) { struct client_thread_cfg *cfg = arg; + struct client_config *ccfg; struct client_thread *ct; int rc; @@ -373,17 +370,20 @@ static void *client_pthread_main(void *arg) ct = talloc_zero(OTC_GLOBAL, struct client_thread); OSMO_ASSERT(ct); + ccfg = client_config_init(ct); + OSMO_ASSERT(ccfg); + osmo_talloc_replace_string(ccfg, &ccfg->server_host, cfg->server_host); + if (cfg->server_port >= 0) + ccfg->server_port = cfg->server_port; + ccfg->client_id = cfg->client_id; + ccfg->client_slot = cfg->client_slot; + if (!talloc_asn1_ctx) talloc_asn1_ctx= talloc_named_const(ct, 0, "asn1"); - ct->bc = remsim_client_create(ct, cfg->name, "remsim_ifdhandler"); + ct->bc = remsim_client_create(ct, cfg->name, "remsim_ifdhandler", ccfg); OSMO_ASSERT(ct->bc); ct->bc->data = ct; - remsim_client_set_clslot(ct->bc, cfg->client_id, cfg->client_slot); - if (cfg->server_host) - ct->bc->srv_conn.server_host = (char *) cfg->server_host; - if (cfg->server_port >= 0) - ct->bc->srv_conn.server_port = cfg->server_port; INIT_LLIST_HEAD(&ct->it_msgq); osmo_fd_setup(&ct->it_ofd, cfg->it_sock_fd, OSMO_FD_READ, &it_sock_fd_cb, ct, 0); diff --git a/src/client/user_shell.c b/src/client/user_shell.c index abe1542..40bcddd 100644 --- a/src/client/user_shell.c +++ b/src/client/user_shell.c @@ -12,58 +12,54 @@ /*********************************************************************** - * Incoming RSPRO messages from bank-daemon (SIM card) + * stdin frontend code to remsim-client ***********************************************************************/ -static int bankd_handle_tpduCardToModem(struct bankd_client *bc, const RsproPDU_t *pdu) +int frontend_request_card_insert(struct bankd_client *bc) { - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_tpduCardToModem == pdu->msg.present); + return 0; +} - const struct TpduCardToModem *card2modem = &pdu->msg.choice.tpduCardToModem; +int frontend_request_sim_remote(struct bankd_client *bc) +{ + return 0; +} - printf("R-APDU: %s\n", osmo_hexdump(card2modem->data.buf, card2modem->data.size)); +int frontend_request_modem_reset(struct bankd_client *bc) +{ + return 0; +} + +int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len) +{ + OSMO_ASSERT(data); + printf("R-APDU: %s\n", osmo_hexdump(data, len)); fflush(stdout); return 0; } -static int bankd_handle_setAtrReq(struct bankd_client *bc, const RsproPDU_t *pdu) +int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len) { - RsproPDU_t *resp; + OSMO_ASSERT(data); - OSMO_ASSERT(pdu); - OSMO_ASSERT(RsproPDUchoice_PR_setAtrReq == pdu->msg.present); - - printf("SET_ATR: %s\n", osmo_hexdump(pdu->msg.choice.setAtrReq.atr.buf, - pdu->msg.choice.setAtrReq.atr.size)); + printf("SET_ATR: %s\n", osmo_hexdump(data, len)); fflush(stdout); - resp = rspro_gen_SetAtrRes(ResultCode_ok); - if (!resp) - return -ENOMEM; - server_conn_send_rspro(&bc->bankd_conn, resp); - return 0; } - -int client_user_bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu) +int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts) { - struct bankd_client *client = bankdc2bankd_client(bankdc); - switch (pdu->msg.present) { - case RsproPDUchoice_PR_tpduCardToModem: - bankd_handle_tpduCardToModem(client, pdu); - break; - case RsproPDUchoice_PR_setAtrReq: - bankd_handle_setAtrReq(client, pdu); - break; - default: - OSMO_ASSERT(0); - } return 0; } +int frontend_append_script_env(struct bankd_client *bc, char **env, size_t max_env) +{ + return 0; +} + + /*********************************************************************** * Incoming command from the user application (stdin shell in our case) ***********************************************************************/ diff --git a/src/client/user_simtrace2.c b/src/client/user_simtrace2.c new file mode 100644 index 0000000..24a8d09 --- /dev/null +++ b/src/client/user_simtrace2.c @@ -0,0 +1,458 @@ +/* (C) 2018-2020 by Harald Welte + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 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 +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include "client.h" +#include "debug.h" + + +/*********************************************************************** + * Incoming Messages from cardem firmware + ***********************************************************************/ + +/*! \brief Process a STATUS message from the SIMtrace2 */ +static int process_do_status(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len) +{ + struct cardemu_usb_msg_status *status; + status = (struct cardemu_usb_msg_status *) buf; + + printf("SIMtrace => STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n", + status->flags, status->fi, status->di, status->wi, + status->waiting_time); + + return 0; +} + +/*! \brief Process a PTS indication message from the SIMtrace2 */ +static int process_do_pts(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len) +{ + struct cardemu_usb_msg_pts_info *pts = (struct cardemu_usb_msg_pts_info *) buf; + struct bankd_client *bc = ci->priv; + struct frontend_pts fpts = { + .buf = pts->req, + .len = sizeof(pts->req), + }; + + printf("SIMtrace => PTS req: %s\n", osmo_hexdump(pts->req, sizeof(pts->req))); + + osmo_fsm_inst_dispatch(bc->main_fi, MF_E_MDM_PTS_IND, &fpts); + + return 0; +} + +/*! \brief Process a ERROR indication message from the SIMtrace2 */ +__attribute__((unused)) static int process_do_error(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len) +{ + struct cardemu_usb_msg_error *err; + err = (struct cardemu_usb_msg_error *) buf; + + printf("SIMtrace => ERROR: %u/%u/%u: %s\n", + err->severity, err->subsystem, err->code, + err->msg_len ? (char *)err->msg : ""); + + return 0; +} + +static struct osmo_apdu_context ac; // this will hold the complete APDU (across calls) + +/*! \brief Process a RX-DATA indication message from the SIMtrace2 */ +static int process_do_rx_da(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len) +{ + struct cardemu_usb_msg_rx_data *data = (struct cardemu_usb_msg_rx_data *) buf; + struct bankd_client *bc = ci->priv; + struct frontend_tpdu ftpdu; + int rc; + + printf("SIMtrace => DATA: flags=%x, %s: ", data->flags, + osmo_hexdump(data->data, data->data_len)); + + /* parse the APDU data in the USB message */ + rc = osmo_apdu_segment_in(&ac, data->data, data->data_len, + data->flags & CEMU_DATA_F_TPDU_HDR); + + if (rc & APDU_ACT_TX_CAPDU_TO_CARD) { + /* there is no pending data coming from the modem */ + uint8_t apdu_command[sizeof(ac.hdr) + ac.lc.tot]; + memcpy(apdu_command, &ac.hdr, sizeof(ac.hdr)); + if (ac.lc.tot) + memcpy(apdu_command + sizeof(ac.hdr), ac.dc, ac.lc.tot); + /* send APDU to card */ + ftpdu.buf = apdu_command; + ftpdu.len = sizeof(ac.hdr) + ac.lc.tot; + osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_TPDU, &ftpdu); + } else if (ac.lc.tot > ac.lc.cur) { + /* there is pending data from the modem: send procedure byte to get remaining data */ + osmo_st2_cardem_request_pb_and_rx(ci, ac.hdr.ins, ac.lc.tot - ac.lc.cur); + } + return 0; +} + +#if 0 + case SIMTRACE_CMD_DO_ERROR + rc = process_do_error(ci, buf, len); + break; +#endif + +/*! \brief Process an incoming message from the SIMtrace2 */ +static int process_usb_msg(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len) +{ + struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf; + int rc; + + printf("SIMtrace -> %s\n", osmo_hexdump(buf, len)); + + buf += sizeof(*sh); + + switch (sh->msg_type) { + case SIMTRACE_MSGT_BD_CEMU_STATUS: + rc = process_do_status(ci, buf, len); + break; + case SIMTRACE_MSGT_DO_CEMU_PTS: + rc = process_do_pts(ci, buf, len); + break; + case SIMTRACE_MSGT_DO_CEMU_RX_DATA: + rc = process_do_rx_da(ci, buf, len); + break; + case SIMTRACE_MSGT_BD_CEMU_CONFIG: + /* firmware confirms configuration change; ignore */ + break; + default: + printf("unknown simtrace msg type 0x%02x\n", sh->msg_type); + rc = -1; + break; + } + + return rc; +} + + +/*! \brief Process a STATUS message on IRQ endpoint from the SIMtrace2 */ +static int process_irq_status(struct osmo_st2_cardem_inst *ci, const uint8_t *buf, int len) +{ + const struct cardemu_usb_msg_status *status = (struct cardemu_usb_msg_status *) buf; + struct bankd_client *bc = ci->priv; + struct frontend_phys_status pstatus = { + .flags = { + .reset_active = status->flags & CEMU_STATUS_F_RESET_ACTIVE, + .vcc_present = status->flags & CEMU_STATUS_F_VCC_PRESENT, + .clk_active = status->flags & CEMU_STATUS_F_CLK_ACTIVE, + .card_present = -1 /* FIXME: make this dependent on board */, + }, + .voltage_mv = status->voltage_mv, + .fi = status->fi, + .di = status->di, + .wi = status->wi, + .waiting_time = status->waiting_time, + }; + + printf("SIMtrace IRQ STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n", + status->flags, status->fi, status->di, status->wi, + status->waiting_time); + + osmo_fsm_inst_dispatch(bc->main_fi, MF_E_MDM_STATUS_IND, &pstatus); + return 0; +} + +static int process_usb_msg_irq(struct osmo_st2_cardem_inst *ci, const uint8_t *buf, unsigned int len) +{ + struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf; + int rc; + + printf("SIMtrace IRQ %s\n", osmo_hexdump(buf, len)); + + buf += sizeof(*sh); + + switch (sh->msg_type) { + case SIMTRACE_MSGT_BD_CEMU_STATUS: + rc = process_irq_status(ci, buf, len); + break; + default: + printf("unknown simtrace msg type 0x%02x\n", sh->msg_type); + rc = -1; + break; + } + + return rc; +} + +static void usb_in_xfer_cb(struct libusb_transfer *xfer) +{ + struct osmo_st2_cardem_inst *ci = xfer->user_data; + int rc; + + switch (xfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + /* hand the message up the stack */ + process_usb_msg(ci, xfer->buffer, xfer->actual_length); + break; + case LIBUSB_TRANSFER_NO_DEVICE: + fprintf(stderr, "USB device disappeared\n"); + exit(1); + break; + default: + fprintf(stderr, "USB IN transfer failed, status=%u\n", xfer->status); + exit(1); + break; + } + + /* re-submit the IN transfer */ + rc = libusb_submit_transfer(xfer); + OSMO_ASSERT(rc == 0); +} + + +static void allocate_and_submit_in(struct osmo_st2_cardem_inst *ci) +{ + struct osmo_st2_transport *transp = ci->slot->transp; + struct libusb_transfer *xfer; + int rc; + + xfer = libusb_alloc_transfer(0); + OSMO_ASSERT(xfer); + xfer->dev_handle = transp->usb_devh; + xfer->flags = 0; + xfer->type = LIBUSB_TRANSFER_TYPE_BULK; + xfer->endpoint = transp->usb_ep.in; + xfer->timeout = 0; + xfer->user_data = ci; + xfer->length = 16*256; + + xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length); + OSMO_ASSERT(xfer->buffer); + xfer->callback = usb_in_xfer_cb; + + /* submit the IN transfer */ + rc = libusb_submit_transfer(xfer); + OSMO_ASSERT(rc == 0); +} + + +static void usb_irq_xfer_cb(struct libusb_transfer *xfer) +{ + struct osmo_st2_cardem_inst *ci = xfer->user_data; + int rc; + + switch (xfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + process_usb_msg_irq(ci, xfer->buffer, xfer->actual_length); + break; + case LIBUSB_TRANSFER_NO_DEVICE: + fprintf(stderr, "USB device disappeared\n"); + exit(1); + break; + default: + fprintf(stderr, "USB IRQ transfer failed, status=%u\n", xfer->status); + exit(1); + break; + } + + /* re-submit the IN transfer */ + rc = libusb_submit_transfer(xfer); + OSMO_ASSERT(rc == 0); +} + + +static void allocate_and_submit_irq(struct osmo_st2_cardem_inst *ci) +{ + struct osmo_st2_transport *transp = ci->slot->transp; + struct libusb_transfer *xfer; + int rc; + + xfer = libusb_alloc_transfer(0); + OSMO_ASSERT(xfer); + xfer->dev_handle = transp->usb_devh; + xfer->flags = 0; + xfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + xfer->endpoint = transp->usb_ep.irq_in; + xfer->timeout = 0; + xfer->user_data = ci; + xfer->length = 64; + + xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length); + OSMO_ASSERT(xfer->buffer); + xfer->callback = usb_irq_xfer_cb; + + /* submit the IN transfer */ + rc = libusb_submit_transfer(xfer); + OSMO_ASSERT(rc == 0); +} + + + + +/*********************************************************************** + * simtrace2 frontend code to remsim-client + ***********************************************************************/ + +int frontend_request_card_insert(struct bankd_client *bc) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + return osmo_st2_cardem_request_card_insert(ci, true); +} + +int frontend_request_sim_remote(struct bankd_client *bc) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + return osmo_st2_modem_sim_select_remote(ci->slot); +} + +int frontend_request_modem_reset(struct bankd_client *bc) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + return osmo_st2_modem_reset_pulse(ci->slot, 300); +} + +int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + // save SW to our current APDU context + ac.sw[0] = data[len-2]; + ac.sw[1] = data[len=1]; + + printf("SIMtrace <= SW=0x%02x%02x, len_rx=%zu\n", ac.sw[0], ac.sw[1], len-2); + if (len > 2) { // send PB and data to modem + osmo_st2_cardem_request_pb_and_tx(ci, ac.hdr.ins, data, len-2); + } + osmo_st2_cardem_request_sw_tx(ci, ac.sw); // send SW to modem + return 0; +} + +int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + return osmo_st2_cardem_request_set_atr(ci, data, len); +} + +int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts) +{ + /* we currently don't propagate bankd status to cardem */ + return 0; +} + +int frontend_append_script_env(struct bankd_client *bc, char **env, size_t max_env) +{ + struct osmo_st2_cardem_inst *ci = bc->cardem; + int i = 0; + + if (max_env < 4) + return -ENOSPC; + + env[i++] = talloc_asprintf(env, "REMSIM_USB_PATH=%s", ci->usb_path); + /* TODO: Configuration; Altsetting */ + env[i++] = talloc_asprintf(env, "REMSIM_USB_INTERFACE=%u", bc->cfg->usb.if_num); + + return i; +} + +/* FIXME: This must be cleaned up */ +static struct osmo_st2_transport _transp; +static struct osmo_st2_slot _slot = { + .transp = &_transp, + .slot_nr = 0, +}; + +int client_user_main(struct bankd_client *bc) +{ + struct usb_interface_match _ifm, *ifm = &_ifm; + struct osmo_st2_transport *transp; + struct osmo_st2_cardem_inst *ci; + struct client_config *cfg = bc->cfg; + int rc, i; + + rc = osmo_libusb_init(NULL); + if (rc < 0) { + fprintf(stderr, "libusb initialization failed\n"); + return rc; + } + + ci = talloc_zero(bc, struct osmo_st2_cardem_inst); + OSMO_ASSERT(ci); + ci->slot = &_slot; + transp = ci->slot->transp; + ci->priv = bc; + bc->cardem = ci; + + ifm->vendor = cfg->usb.vendor_id; + ifm->product = cfg->usb.product_id; + ifm->configuration = cfg->usb.config_id; + ifm->interface = cfg->usb.if_num; + ifm->altsetting = cfg->usb.altsetting; + ifm->addr = cfg->usb.addr; + if (cfg->usb.path) + osmo_strlcpy(ifm->path, cfg->usb.path, sizeof(ifm->path)); + transp->usb_devh = osmo_libusb_open_claim_interface(NULL, NULL, ifm); + if (!transp->usb_devh) { + fprintf(stderr, "can't open USB device\n"); + return -1; + } + + /* (re)determine the USB path of the opened device */ + talloc_free(ci->usb_path); + ci->usb_path = osmo_libusb_dev_get_path_c(ci, libusb_get_device(transp->usb_devh)); + + rc = libusb_claim_interface(transp->usb_devh, cfg->usb.if_num); + if (rc < 0) { + fprintf(stderr, "can't claim interface %d; rc=%d\n", cfg->usb.if_num, rc); + goto close_exit; + } + + rc = osmo_libusb_get_ep_addrs(transp->usb_devh, cfg->usb.if_num, &transp->usb_ep.out, + &transp->usb_ep.in, &transp->usb_ep.irq_in); + if (rc < 0) { + fprintf(stderr, "can't obtain EP addrs; rc=%d\n", rc); + goto close_exit; + } + + allocate_and_submit_irq(ci); + /* submit multiple IN URB in order to work around OS#4409 */ + for (i = 0; i < 4; i++) + allocate_and_submit_in(ci); + + /* request firmware to generate STATUS on IRQ endpoint */ + osmo_st2_cardem_request_config(ci, CEMU_FEAT_F_STATUS_IRQ); + + while (1) { + osmo_select_main(0); + } + + return 0; + +close_exit: + if (transp->usb_devh) + libusb_close(transp->usb_devh); + osmo_libusb_exit(NULL); + + return -1; +} diff --git a/src/rspro_client_fsm.c b/src/rspro_client_fsm.c index e55892c..36a3016 100644 --- a/src/rspro_client_fsm.c +++ b/src/rspro_client_fsm.c @@ -397,7 +397,7 @@ static const struct osmo_fsm_state server_conn_fsm_states[] = { [SRVC_ST_INIT] = { .name = "INIT", .in_event_mask = 0, /* S(SRVC_E_ESTABLISH) via allstate */ - .out_state_mask = S(SRVC_ST_REESTABLISH), + .out_state_mask = S(SRVC_ST_INIT) | S(SRVC_ST_REESTABLISH), .action = srvc_st_init, }, [SRVC_ST_ESTABLISHED] = {