client: major restructuring around new main_fsm

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: I44a430bc5674dea00ed72a0b28729ac8bcb4e022
changes/61/17361/2
Harald Welte 3 years ago
parent 5b5d6cee9b
commit 0e968cce38
  1. 9
      src/client/Makefile.am
  2. 78
      src/client/client.h
  3. 377
      src/client/main_fsm.c
  4. 76
      src/client/remsim_client.c
  5. 87
      src/client/remsim_client_main.c
  6. 1284
      src/client/simtrace2-remsim_client.c
  7. 80
      src/client/user_ifdhandler.c
  8. 56
      src/client/user_shell.c
  9. 458
      src/client/user_simtrace2.c
  10. 2
      src/rspro_client_fsm.c

@ -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) \

@ -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_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);
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);

@ -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);
}

@ -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 <errno.h>
#include <string.h>
@ -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;
}

@ -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 <path> 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);

File diff suppressed because it is too large Load Diff

@ -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);

@ -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;
}
int frontend_request_modem_reset(struct bankd_client *bc)
{
return 0;
}
printf("R-APDU: %s\n", osmo_hexdump(card2modem->data.buf, card2modem->data.size));
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(pdu);
OSMO_ASSERT(RsproPDUchoice_PR_setAtrReq == pdu->msg.present);
OSMO_ASSERT(data);
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 frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts)
{
return 0;
}
int client_user_bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu)
int frontend_append_script_env(struct bankd_client *bc, char **env, size_t max_env)
{
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;
}
/***********************************************************************
* Incoming command from the user application (stdin shell in our case)
***********************************************************************/

@ -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;