The remsim_client code already used FSMs for the connections to both remsim-server and remsim-bankd. However the 'main' part of the program was not yet implemented as a FSM, making it somewhat difficult to perform the right actions in every possible situation. This commit re-structures the code around a central main_fsm, which gets notified from the per-connection FSMs and which handles the common processing. It also handles the execution of external script commands, and hence further unifies the code base between the different backends (simtrace2, ifd_handler, shell) Closes: #4414 Change-Id: I44a430bc5674dea00ed72a0b28729ac8bcb4e022changes/61/17361/2
parent
5b5d6cee9b
commit
0e968cce38
@ -0,0 +1,377 @@ |
||||
/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* |
||||
* 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 <stdint.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
|
||||
#include <osmocom/core/talloc.h> |
||||
#include <osmocom/core/logging.h> |
||||
#include <osmocom/core/utils.h> |
||||
#include <osmocom/core/fsm.h> |
||||
#include <osmocom/core/exec.h> |
||||
|
||||
#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); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,458 @@ |
||||
/* (C) 2018-2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* |
||||
* 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 <stdio.h> |
||||
#include <errno.h> |
||||
|
||||
#include <libusb.h> |
||||
|
||||
#include <osmocom/core/fsm.h> |
||||
#include <osmocom/core/utils.h> |
||||
|
||||
#include <osmocom/usb/libusb.h> |
||||
|
||||
#include <osmocom/simtrace2/apdu_dispatch.h> |
||||
#include <osmocom/simtrace2/simtrace2_api.h> |
||||
#include <osmocom/simtrace2/simtrace_prot.h> |
||||
|
||||
#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; |
||||
|