/* Code providing a ccid_slot_ops implementation based on iso7716_fsm, * (which in turn sits on top of card_uart) */ #include #include #include #include #include #include #include #include "ccid_device.h" #include "cuart.h" #include "iso7816_fsm.h" #include "iso7816_3.h" struct iso_fsm_slot { /* CCID slot above us */ struct ccid_slot *cs; /* main ISO7816-3 FSM instance beneath us */ struct osmo_fsm_inst *fi; /* UART beneath the ISO7816-3 FSM */ struct card_uart *cuart; /* bSeq of the operation currently in progress */ uint8_t seq; }; struct iso_fsm_slot_instance { struct iso_fsm_slot slot[NR_SLOTS]; }; static struct iso_fsm_slot_instance g_si; static struct iso_fsm_slot *ccid_slot2iso_fsm_slot(struct ccid_slot *cs) { OSMO_ASSERT(cs->slot_nr < ARRAY_SIZE(g_si.slot)); return &g_si.slot[cs->slot_nr]; } struct card_uart *cuart4slot_nr(uint8_t slot_nr) { OSMO_ASSERT(slot_nr < ARRAY_SIZE(g_si.slot)); return g_si.slot[slot_nr].cuart; } static const uint8_t sysmousim_sjs1_atr[] = { 0x3B, 0x9F, 0x96, 0x80, 0x1F, 0xC7, 0x80, 0x31, 0xA0, 0x73, 0xBE, 0x21, 0x13, 0x67, 0x43, 0x20, 0x07, 0x18, 0x00, 0x00, 0x01, 0xA5 }; static const struct ccid_pars_decoded iso_fsm_def_pars = { .fi = 372, .di = 1, .clock_stop = CCID_CLOCK_STOP_NOTALLOWED, .inverse_convention = false, .t0 = { .guard_time_etu = 0, .waiting_integer = 0, }, /* FIXME: T=1 */ }; static void iso_fsm_slot_pre_proc_cb(struct ccid_slot *cs, struct msgb *msg) { /* do nothing; real hardware would update the slot related state here */ } static void iso_fsm_slot_icc_set_insertion_status(struct ccid_slot *cs, bool present) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); if(present == cs->icc_present) return; cs->icc_present = present; if (!present) { osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_CARD_REMOVAL, NULL); card_uart_ctrl(ss->cuart, CUART_CTL_RST, true); card_uart_ctrl(ss->cuart, CUART_CTL_POWER, false); cs->icc_powered = false; cs->cmd_busy = false; } } static void iso_fsm_slot_icc_power_on_async(struct ccid_slot *cs, struct msgb *msg, const struct ccid_pc_to_rdr_icc_power_on *ipo) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); ss->seq = ipo->hdr.bSeq; LOGPCS(cs, LOGL_DEBUG, "scheduling power-up\n"); /* FIXME: do this via a FSM? */ card_uart_ctrl(ss->cuart, CUART_CTL_RST, true); osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_RESET_ACT_IND, NULL); card_uart_ctrl(ss->cuart, CUART_CTL_POWER, true); osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_POWER_UP_IND, NULL); cs->icc_powered = true; card_uart_ctrl(ss->cuart, CUART_CTL_CLOCK, true); #ifdef OCTSIMFWBUILD delay_us(10000); #else usleep(10000); #endif osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_RESET_REL_IND, NULL); card_uart_ctrl(ss->cuart, CUART_CTL_RST, false); msgb_free(msg); /* continues in iso_fsm_clot_user_cb once ATR is received */ } static void iso_fsm_clot_user_cb(struct osmo_fsm_inst *fi, int event, int cause, void *data) { struct iso_fsm_slot *ss = iso7816_fsm_get_user_priv(fi); struct ccid_slot *cs = ss->cs; switch (event) { case ISO7816_E_ATR_DONE_IND: case ISO7816_E_ATR_ERR_IND: case ISO7816_E_TPDU_DONE_IND: case ISO7816_E_TPDU_FAILED_IND: case ISO7816_E_PPS_DONE_IND: case ISO7816_E_PPS_FAILED_IND: cs->event_data = data; #ifdef OCTSIMFWBUILD asm volatile("dmb st": : :"memory"); #endif cs->event = event; break; default: LOGPCS(cs, LOGL_NOTICE, "%s(event=%d, cause=%d, data=%p) unhandled\n", __func__, event, cause, data); break; } } static int iso_handle_fsm_events(struct ccid_slot *cs, bool enable){ struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); struct msgb *tpdu, *resp; volatile uint32_t event = cs->event; volatile void * volatile data = cs->event_data; if(!event) return 0; if(event && !data) return 0; switch (event) { case ISO7816_E_ATR_DONE_IND: tpdu = data; LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_OK, 0, msgb_data(tpdu), msgb_length(tpdu)); ccid_slot_send_unbusy(cs, resp); /* Don't free "TPDU" here, as the ATR should survive */ cs->event = 0; break; case ISO7816_E_ATR_ERR_IND: tpdu = data; LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_FAILED, CCID_ERR_ICC_MUTE, msgb_data(tpdu), msgb_length(tpdu)); ccid_slot_send_unbusy(cs, resp); /* Don't free "TPDU" here, as the ATR should survive */ cs->event = 0; break; break; case ISO7816_E_TPDU_DONE_IND: tpdu = data; LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_OK, 0, msgb_l2(tpdu), msgb_l2len(tpdu)); ccid_slot_send_unbusy(cs, resp); msgb_free(tpdu); cs->event = 0; break; case ISO7816_E_TPDU_FAILED_IND: tpdu = data; LOGPCS(cs, LOGL_DEBUG, "%s(event=%d, data=%s)\n", __func__, event, msgb_hexdump(tpdu)); /* FIXME: other error causes than card removal?*/ resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_FAILED, CCID_ERR_ICC_MUTE, msgb_l2(tpdu), 0); ccid_slot_send_unbusy(cs, resp); msgb_free(tpdu); cs->event = 0; break; case ISO7816_E_PPS_DONE_IND: tpdu = data; /* pps was successful, so we know these values are fine */ uint16_t F = iso7816_3_fi_table[cs->proposed_pars.fi]; uint8_t D = iso7816_3_di_table[cs->proposed_pars.di]; uint32_t fmax = iso7816_3_fmax_table[cs->proposed_pars.fi]; /* 7816-3 5.2.3 * No information shall be exchanged when switching the * frequency value. Two different times are recommended * for switching the frequency value, either * - after ATR while card is idle * - after PPS while card is idle */ card_uart_ctrl(ss->cuart, CUART_CTL_SET_CLOCK_FREQ, fmax); card_uart_ctrl(ss->cuart, CUART_CTL_SET_FD, F/D); card_uart_ctrl(ss->cuart, CUART_CTL_WTIME, cs->proposed_pars.t0.waiting_integer); cs->pars = cs->proposed_pars; resp = ccid_gen_parameters_t0(cs, ss->seq, CCID_CMD_STATUS_OK, 0); ccid_slot_send_unbusy(cs, resp); /* this frees the pps req from the host, pps resp buffer stays with the pps fsm */ msgb_free(tpdu); cs->event = 0; break; case ISO7816_E_PPS_FAILED_IND: tpdu = data; /* failed fi/di */ resp = ccid_gen_parameters_t0(cs, ss->seq, CCID_CMD_STATUS_FAILED, 10); ccid_slot_send_unbusy(cs, resp); /* this frees the pps req from the host, pps resp buffer stays with the pps fsm */ msgb_free(tpdu); cs->event = 0; break; case 0: break; default: LOGPCS(cs, LOGL_NOTICE, "%s(event=%d, data=%p) unhandled\n", __func__, event, data); break; } } static int iso_fsm_slot_xfr_block_async(struct ccid_slot *cs, struct msgb *msg, const struct ccid_pc_to_rdr_xfr_block *xfb) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); ss->seq = xfb->hdr.bSeq; /* must be '0' for TPDU level exchanges or for short APDU */ OSMO_ASSERT(xfb->wLevelParameter == 0x0000); OSMO_ASSERT(msgb_length(msg) > xfb->hdr.dwLength); msgb_pull(msg, 10); LOGPCS(cs, LOGL_DEBUG, "scheduling TPDU transfer: %s\n", msgb_hexdump(msg)); osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_XCEIVE_TPDU_CMD, msg); /* continues in iso_fsm_clot_user_cb once response/error/timeout is received */ return 0; } static void iso_fsm_slot_set_power(struct ccid_slot *cs, bool enable) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); if (enable) { card_uart_ctrl(ss->cuart, CUART_CTL_POWER, true); cs->icc_powered = true; } else { card_uart_ctrl(ss->cuart, CUART_CTL_POWER, false); cs->icc_powered = false; } } static void iso_fsm_slot_set_clock(struct ccid_slot *cs, enum ccid_clock_command cmd) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); switch (cmd) { case CCID_CLOCK_CMD_STOP: card_uart_ctrl(ss->cuart, CUART_CTL_CLOCK, false); break; case CCID_CLOCK_CMD_RESTART: card_uart_ctrl(ss->cuart, CUART_CTL_CLOCK, true); break; default: OSMO_ASSERT(0); } } static int iso_fsm_slot_set_params(struct ccid_slot *cs, uint8_t seq, enum ccid_protocol_num proto, const struct ccid_pars_decoded *pars_dec) { struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); struct msgb *tpdu; /* see 6.1.7 for error offsets */ if(proto != CCID_PROTOCOL_NUM_T0) return -7; if(pars_dec->t0.guard_time_etu != 0) return -12; if(pars_dec->clock_stop != CCID_CLOCK_STOP_NOTALLOWED) return -14; ss->seq = seq; /* FIXME: When using D=64, the interface device shall ensure a delay of at least 16 etu between the leading edge of the last received character and the leading edge of the character transmitted for initiating a command. -> we can't really do 4 stop bits?! */ /* Hardware does not support SPU, so no PPS2, and PPS3 is reserved anyway */ tpdu = msgb_alloc(6, "PPSRQ"); OSMO_ASSERT(tpdu); msgb_put_u8(tpdu, 0xff); msgb_put_u8(tpdu, (1 << 4)); /* only PPS1, T=0 */ msgb_put_u8(tpdu, (pars_dec->fi << 4 | pars_dec->di)); msgb_put_u8(tpdu, 0xff ^ (1 << 4) ^ (pars_dec->fi << 4 | pars_dec->di)); LOGPCS(cs, LOGL_DEBUG, "scheduling PPS transfer: %s\n", msgb_hexdump(tpdu)); osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_XCEIVE_PPS_CMD, tpdu); /* continues in iso_fsm_clot_user_cb once response/error/timeout is received */ return 0; } static int iso_fsm_slot_set_rate_and_clock(struct ccid_slot *cs, uint32_t freq_hz, uint32_t rate_bps) { /* we always acknowledge all rates/clocks */ return 0; } extern void *g_tall_ctx; static int iso_fsm_slot_init(struct ccid_slot *cs) { void *ctx = g_tall_ctx; /* FIXME */ struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs); struct card_uart *cuart = talloc_zero(ctx, struct card_uart); char id_buf[3+3+1]; char devname[2+1]; char *devnamep = 0; char *drivername = "asf4"; int rc; LOGPCS(cs, LOGL_DEBUG, "%s\n", __func__); snprintf(id_buf, sizeof(id_buf), "SIM%d", cs->slot_nr); #ifdef OCTSIMFWBUILD snprintf(devname, sizeof(devname), "%d", cs->slot_nr); devnamep = devname; #else if (cs->slot_nr == 0) { cs->icc_present = true; devnamep = "/dev/ttyUSB5"; } drivername = "tty"; #endif if (!cuart) return -ENOMEM; if (devnamep) { rc = card_uart_open(cuart, drivername, devnamep); if (rc < 0) { LOGPCS(cs, LOGL_ERROR, "Cannot open UART %s: %d\n", devname, rc); talloc_free(cuart); return rc; } } ss->fi = iso7816_fsm_alloc(ctx, LOGL_DEBUG, id_buf, cuart, iso_fsm_clot_user_cb, ss); if (!ss->fi) { LOGPCS(cs, LOGL_ERROR, "Cannot allocate ISO FSM\n"); talloc_free(cuart); return -1; } cs->default_pars = &iso_fsm_def_pars; ss->cuart = cuart; ss->cs = cs; return 0; } const struct ccid_slot_ops iso_fsm_slot_ops = { .init = iso_fsm_slot_init, .pre_proc_cb = iso_fsm_slot_pre_proc_cb, .icc_power_on_async = iso_fsm_slot_icc_power_on_async, .icc_set_insertion_status = iso_fsm_slot_icc_set_insertion_status, .xfr_block_async = iso_fsm_slot_xfr_block_async, .set_power = iso_fsm_slot_set_power, .set_clock = iso_fsm_slot_set_clock, .set_params = iso_fsm_slot_set_params, .set_rate_and_clock = iso_fsm_slot_set_rate_and_clock, .handle_fsm_events = iso_handle_fsm_events, };