Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 1 | /* ISO7816-3 state machine for the card side |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 2 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 3 | * (C) 2010-2017 by Harald Welte <laforge@gnumonks.org> |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 4 | * (C) 2018-2019 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de> |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 5 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 10 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 15 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 19 | */ |
Harald Welte | c430ac1 | 2017-02-28 01:25:12 +0100 | [diff] [blame] | 20 | #include <stdio.h> |
Harald Welte | 4c473da | 2015-11-14 13:31:11 +0100 | [diff] [blame] | 21 | #include <assert.h> |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 22 | #include <errno.h> |
| 23 | #include <string.h> |
| 24 | #include <stdint.h> |
| 25 | #include <sys/types.h> |
| 26 | |
| 27 | #include "utils.h" |
| 28 | #include "trace.h" |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 29 | #include "iso7816_3.h" |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 30 | #include "card_emu.h" |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 31 | #include "simtrace_prot.h" |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 32 | #include "usb_buf.h" |
Harald Welte | 9d90d28 | 2018-06-29 22:25:42 +0200 | [diff] [blame] | 33 | #include <osmocom/core/linuxlist.h> |
| 34 | #include <osmocom/core/msgb.h> |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 35 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 36 | #define NUM_SLOTS 2 |
| 37 | |
| 38 | #define ISO7816_3_INIT_WTIME 9600 |
| 39 | #define ISO7816_3_DEFAULT_WI 10 |
| 40 | #define ISO7816_3_ATR_LEN_MAX (1+32) /* TS plus 32 chars */ |
| 41 | |
| 42 | #define ISO7816_3_PB_NULL 0x60 |
| 43 | |
| 44 | enum iso7816_3_card_state { |
| 45 | ISO_S_WAIT_POWER, /* waiting for power being applied */ |
| 46 | ISO_S_WAIT_CLK, /* waiting for clock being applied */ |
| 47 | ISO_S_WAIT_RST, /* waiting for reset being released */ |
| 48 | ISO_S_WAIT_ATR, /* waiting for start of ATR */ |
| 49 | ISO_S_IN_ATR, /* transmitting ATR to reader */ |
| 50 | ISO_S_IN_PTS, /* transmitting ATR to reader */ |
| 51 | ISO_S_WAIT_TPDU, /* waiting for data from reader */ |
| 52 | ISO_S_IN_TPDU, /* inside a TPDU */ |
| 53 | }; |
| 54 | |
Kévin Redon | 8a4fba5 | 2018-08-02 17:44:16 +0200 | [diff] [blame] | 55 | const struct value_string iso7816_3_card_state_names[] = { |
Kévin Redon | 2fdcf3b | 2018-10-15 19:43:15 +0200 | [diff] [blame] | 56 | { |
| 57 | .value = ISO_S_WAIT_POWER, |
| 58 | .str = "WAIT_POWER", |
| 59 | }, |
| 60 | { |
| 61 | .value = ISO_S_WAIT_CLK, |
| 62 | .str = "WAIT_CLK", |
| 63 | }, |
| 64 | { |
| 65 | .value = ISO_S_WAIT_RST, |
| 66 | .str = "WAIT_RST", |
| 67 | }, |
| 68 | { |
| 69 | .value = ISO_S_WAIT_ATR, |
| 70 | .str = "WAIT_ATR", |
| 71 | }, |
| 72 | { |
| 73 | .value = ISO_S_IN_ATR, |
| 74 | .str = "IN_ATR", |
| 75 | }, |
| 76 | { |
| 77 | .value = ISO_S_IN_PTS, |
| 78 | .str = "IN_PTS", |
| 79 | }, |
| 80 | { |
| 81 | .value = ISO_S_WAIT_TPDU, |
| 82 | .str = "WAIT_TPDU", |
| 83 | }, |
| 84 | { |
| 85 | .value = ISO_S_IN_TPDU, |
| 86 | .str = "IN_TPDU", |
| 87 | }, |
Kévin Redon | 8a4fba5 | 2018-08-02 17:44:16 +0200 | [diff] [blame] | 88 | { |
| 89 | .value = 0, |
| 90 | .str = NULL, |
| 91 | }, |
| 92 | }; |
| 93 | |
| 94 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 95 | /* detailed sub-states of ISO_S_IN_PTS */ |
| 96 | enum pts_state { |
| 97 | PTS_S_WAIT_REQ_PTSS, |
| 98 | PTS_S_WAIT_REQ_PTS0, |
| 99 | PTS_S_WAIT_REQ_PTS1, |
| 100 | PTS_S_WAIT_REQ_PTS2, |
| 101 | PTS_S_WAIT_REQ_PTS3, |
| 102 | PTS_S_WAIT_REQ_PCK, |
| 103 | PTS_S_WAIT_RESP_PTSS = PTS_S_WAIT_REQ_PTSS | 0x10, |
| 104 | PTS_S_WAIT_RESP_PTS0 = PTS_S_WAIT_REQ_PTS0 | 0x10, |
| 105 | PTS_S_WAIT_RESP_PTS1 = PTS_S_WAIT_REQ_PTS1 | 0x10, |
| 106 | PTS_S_WAIT_RESP_PTS2 = PTS_S_WAIT_REQ_PTS2 | 0x10, |
| 107 | PTS_S_WAIT_RESP_PTS3 = PTS_S_WAIT_REQ_PTS3 | 0x10, |
| 108 | PTS_S_WAIT_RESP_PCK = PTS_S_WAIT_REQ_PCK | 0x10, |
| 109 | }; |
| 110 | |
Kévin Redon | 2fdcf3b | 2018-10-15 19:43:15 +0200 | [diff] [blame] | 111 | /* PTS field byte index */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 112 | #define _PTSS 0 |
| 113 | #define _PTS0 1 |
| 114 | #define _PTS1 2 |
| 115 | #define _PTS2 3 |
| 116 | #define _PTS3 4 |
| 117 | #define _PCK 5 |
| 118 | |
Harald Welte | 16cf408 | 2015-11-11 19:02:48 +0100 | [diff] [blame] | 119 | /* T-PDU state machine states */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 120 | enum tpdu_state { |
Harald Welte | 16cf408 | 2015-11-11 19:02:48 +0100 | [diff] [blame] | 121 | TPDU_S_WAIT_CLA, /* waiting for CLA byte from reader */ |
| 122 | TPDU_S_WAIT_INS, /* waiting for INS byte from reader */ |
| 123 | TPDU_S_WAIT_P1, /* waiting for P1 byte from reader */ |
| 124 | TPDU_S_WAIT_P2, /* waiting for P2 byte from reader */ |
| 125 | TPDU_S_WAIT_P3, /* waiting for P3 byte from reader */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 126 | TPDU_S_WAIT_PB, /* waiting for Tx of procedure byte */ |
Kévin Redon | 0f4abf5 | 2018-08-02 17:42:48 +0200 | [diff] [blame] | 127 | TPDU_S_WAIT_RX, /* waiting for more data from reader */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 128 | TPDU_S_WAIT_TX, /* waiting for more data to reader */ |
| 129 | }; |
| 130 | |
Kévin Redon | 2fdcf3b | 2018-10-15 19:43:15 +0200 | [diff] [blame] | 131 | const struct value_string tpdu_state_names[] = { |
| 132 | { |
| 133 | .value = TPDU_S_WAIT_CLA, |
| 134 | .str = "WAIT_CLA", |
| 135 | }, |
| 136 | { |
| 137 | .value = TPDU_S_WAIT_INS, |
| 138 | .str = "WAIT_INS", |
| 139 | }, |
| 140 | { |
| 141 | .value = TPDU_S_WAIT_P1, |
| 142 | .str = "WAIT_P1", |
| 143 | }, |
| 144 | { |
| 145 | .value = TPDU_S_WAIT_P2, |
| 146 | .str = "WAIT_P2", |
| 147 | }, |
| 148 | { |
| 149 | .value = TPDU_S_WAIT_P3, |
| 150 | .str = "WAIT_P3", |
| 151 | }, |
| 152 | { |
| 153 | .value = TPDU_S_WAIT_PB, |
| 154 | .str = "WAIT_PB", |
| 155 | }, |
| 156 | { |
| 157 | .value = TPDU_S_WAIT_RX, |
| 158 | .str = "WAIT_RX", |
| 159 | }, |
| 160 | { |
| 161 | .value = TPDU_S_WAIT_TX, |
| 162 | .str = "WAIT_TX", |
| 163 | }, |
| 164 | { |
| 165 | .value = 0, |
| 166 | .str = NULL, |
| 167 | }, |
| 168 | }; |
| 169 | |
| 170 | /* TPDU field byte index */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 171 | #define _CLA 0 |
| 172 | #define _INS 1 |
| 173 | #define _P1 2 |
| 174 | #define _P2 3 |
| 175 | #define _P3 4 |
| 176 | |
| 177 | struct card_handle { |
Harald Welte | c394109 | 2018-08-26 09:53:13 +0200 | [diff] [blame] | 178 | unsigned int num; |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 179 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 180 | enum iso7816_3_card_state state; |
| 181 | |
| 182 | /* signal levels */ |
| 183 | uint8_t vcc_active; /* 1 = on, 0 = off */ |
| 184 | uint8_t in_reset; /* 1 = RST low, 0 = RST high */ |
| 185 | uint8_t clocked; /* 1 = active, 0 = inactive */ |
| 186 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 187 | uint8_t tc_chan; /* TC channel number */ |
| 188 | uint8_t uart_chan; /* UART channel */ |
| 189 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 190 | uint8_t in_ep; /* USB IN EP */ |
| 191 | uint8_t irq_ep; /* USB IN EP */ |
| 192 | |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 193 | /*! clock rate conversion integer F |
| 194 | * @implements ISO/IEC 7816-3:2006(E) section 7.1 |
| 195 | * @note this represents the current value used |
| 196 | */ |
| 197 | uint16_t f; |
| 198 | /*! baud rate adjustment factor D |
| 199 | * @implements ISO/IEC 7816-3:2006(E) section 7.1 |
| 200 | * @note this represents the current value used |
| 201 | */ |
| 202 | uint8_t d; |
| 203 | /*! clock frequency in Hz |
| 204 | * @implements ISO/IEC 7816-3:2006(E) section 7.1 |
| 205 | * @note the USART peripheral in slave mode does not provide the current value. we could measure it but this is not really useful. instead we remember the maximum possible value corresponding to the selected F value |
| 206 | */ |
| 207 | uint32_t f_cur; |
| 208 | /*! clock rate conversion integer Fi |
| 209 | * @implements ISO/IEC 7816-3:2006(E) Table 7 |
| 210 | * @note this represents the maximum value supported by the card, and can be indicated in TA1 |
| 211 | * @note this value can be set in TA1 |
| 212 | */ |
| 213 | uint16_t fi; |
| 214 | /*! baud rate adjustment factor Di |
| 215 | * @implements ISO/IEC 7816-3:2006(E) Table 8 |
| 216 | * @note this represents the maximum value supported by the card, and can be indicated in TA1 |
| 217 | */ |
| 218 | uint8_t di; |
| 219 | /*! clock frequency, in Hz |
| 220 | * @implements ISO/IEC 7816-3:2006(E) Table 7 |
| 221 | * @note this represents the maximum value supported by the card, and can be indicated in TA1 |
| 222 | */ |
| 223 | uint32_t f_max; |
| 224 | /*! Waiting Integer |
| 225 | * @implements ISO/IEC 7816-3:2006(E) Section 10.2 |
| 226 | * @note this value can be set in TA2 |
| 227 | */ |
| 228 | uint8_t wi; |
| 229 | /*! Waiting Time, in ETU |
| 230 | * @implements ISO/IEC 7816-3:2006(E) Section 8.1 |
| 231 | * @note this depends on Fi, Di, and WI if T=0 is used |
| 232 | */ |
| 233 | uint32_t wt; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 234 | |
| 235 | /* ATR state machine */ |
| 236 | struct { |
| 237 | uint8_t idx; |
| 238 | uint8_t len; |
| 239 | //uint8_t hist_len; |
| 240 | //uint8_t last_td; |
| 241 | uint8_t atr[ISO7816_3_ATR_LEN_MAX]; |
| 242 | } atr; |
| 243 | |
| 244 | /* PPS / PTS support */ |
| 245 | struct { |
| 246 | enum pts_state state; |
Harald Welte | 16cf408 | 2015-11-11 19:02:48 +0100 | [diff] [blame] | 247 | uint8_t req[6]; /* request bytes */ |
| 248 | uint8_t resp[6]; /* response bytes */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 249 | } pts; |
| 250 | |
| 251 | /* TPDU */ |
| 252 | struct { |
| 253 | enum tpdu_state state; |
Harald Welte | 16cf408 | 2015-11-11 19:02:48 +0100 | [diff] [blame] | 254 | uint8_t hdr[5]; /* CLA INS P1 P2 P3 */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 255 | } tpdu; |
| 256 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 257 | struct msgb *uart_rx_msg; /* UART RX -> USB TX */ |
| 258 | struct msgb *uart_tx_msg; /* USB RX -> UART TX */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 259 | |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 260 | struct llist_head uart_tx_queue; |
| 261 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 262 | struct { |
| 263 | uint32_t tx_bytes; |
| 264 | uint32_t rx_bytes; |
| 265 | uint32_t pps; |
| 266 | } stats; |
| 267 | }; |
| 268 | |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 269 | struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch) |
| 270 | { |
| 271 | return &ch->uart_tx_queue; |
| 272 | } |
| 273 | |
Harald Welte | 2935b3c | 2015-11-14 20:00:14 +0100 | [diff] [blame] | 274 | static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts); |
Harald Welte | e7194ab | 2015-11-14 21:03:25 +0100 | [diff] [blame] | 275 | static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss); |
Harald Welte | 2935b3c | 2015-11-14 20:00:14 +0100 | [diff] [blame] | 276 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 277 | /* update simtrace header msg_len and submit USB buffer */ |
| 278 | void usb_buf_upd_len_and_submit(struct msgb *msg) |
| 279 | { |
Harald Welte | b91f6ad | 2017-05-10 22:51:30 +0200 | [diff] [blame] | 280 | struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h; |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 281 | |
| 282 | sh->msg_len = msgb_length(msg); |
| 283 | |
| 284 | usb_buf_submit(msg); |
| 285 | } |
| 286 | |
| 287 | /* Allocate USB buffer and push + initialize simtrace_msg_hdr */ |
| 288 | struct msgb *usb_buf_alloc_st(uint8_t ep, uint8_t msg_class, uint8_t msg_type) |
| 289 | { |
| 290 | struct msgb *msg; |
| 291 | struct simtrace_msg_hdr *sh; |
| 292 | |
| 293 | msg = usb_buf_alloc(ep); |
| 294 | if (!msg) |
| 295 | return NULL; |
| 296 | |
| 297 | msg->l1h = msgb_put(msg, sizeof(*sh)); |
| 298 | sh = (struct simtrace_msg_hdr *) msg->l1h; |
| 299 | memset(sh, 0, sizeof(*sh)); |
| 300 | sh->msg_class = msg_class; |
| 301 | sh->msg_type = msg_type; |
| 302 | msg->l2h = msg->l1h + sizeof(*sh); |
| 303 | |
| 304 | return msg; |
| 305 | } |
| 306 | |
Kévin Redon | 680bdab | 2018-07-18 14:00:57 +0200 | [diff] [blame] | 307 | /* Update cardemu_usb_msg_rx_data length + submit buffer */ |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 308 | static void flush_rx_buffer(struct card_handle *ch) |
| 309 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 310 | struct msgb *msg; |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 311 | struct cardemu_usb_msg_rx_data *rd; |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 312 | uint32_t data_len; |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 313 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 314 | msg = ch->uart_rx_msg; |
| 315 | if (!msg) |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 316 | return; |
| 317 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 318 | ch->uart_rx_msg = NULL; |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 319 | |
Kévin Redon | 680bdab | 2018-07-18 14:00:57 +0200 | [diff] [blame] | 320 | /* store length of data payload field in header */ |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 321 | rd = (struct cardemu_usb_msg_rx_data *) msg->l2h; |
| 322 | rd->data_len = msgb_l2len(msg) - sizeof(*rd); |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 323 | |
Kévin Redon | 910e683 | 2018-08-02 17:47:57 +0200 | [diff] [blame] | 324 | TRACE_INFO("%u: %s (%u)\n\r", |
| 325 | ch->num, __func__, rd->data_len); |
| 326 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 327 | usb_buf_upd_len_and_submit(msg); |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 328 | } |
| 329 | |
Kévin Redon | 680bdab | 2018-07-18 14:00:57 +0200 | [diff] [blame] | 330 | /* convert a non-contiguous PTS request/response into a contiguous |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 331 | * buffer, returning the number of bytes used in the buffer */ |
| 332 | static int serialize_pts(uint8_t *out, const uint8_t *in) |
| 333 | { |
| 334 | int i = 0; |
| 335 | |
| 336 | out[i++] = in[_PTSS]; |
| 337 | out[i++] = in[_PTS0]; |
| 338 | if (in[_PTS0] & (1 << 4)) |
| 339 | out[i++] = in[_PTS1]; |
| 340 | if (in[_PTS0] & (1 << 5)) |
| 341 | out[i++] = in[_PTS2]; |
| 342 | if (in[_PTS0] & (1 << 6)) |
| 343 | out[i++] = in[_PTS3]; |
| 344 | out[i++] = in[_PCK]; |
| 345 | |
| 346 | return i; |
| 347 | } |
| 348 | |
Harald Welte | 17db2f1 | 2016-02-26 09:48:57 +0100 | [diff] [blame] | 349 | static uint8_t csum_pts(const uint8_t *in) |
| 350 | { |
| 351 | uint8_t out[6]; |
| 352 | int len = serialize_pts(out, in); |
| 353 | uint8_t csum = 0; |
| 354 | int i; |
| 355 | |
| 356 | /* we don't include the PCK byte in the checksumming process */ |
| 357 | len -= 1; |
| 358 | |
| 359 | for (i = 0; i < len; i++) |
| 360 | csum = csum ^ out[i]; |
| 361 | |
| 362 | return csum; |
| 363 | } |
| 364 | |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 365 | static void flush_pts(struct card_handle *ch) |
| 366 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 367 | struct msgb *msg; |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 368 | struct cardemu_usb_msg_pts_info *ptsi; |
| 369 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 370 | msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DO_CEMU_PTS); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 371 | if (!msg) |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 372 | return; |
| 373 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 374 | ptsi = (struct cardemu_usb_msg_pts_info *) msgb_put(msg, sizeof(*ptsi)); |
Harald Welte | d295b92 | 2016-03-18 21:01:36 +0100 | [diff] [blame] | 375 | ptsi->pts_len = serialize_pts(ptsi->req, ch->pts.req); |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 376 | serialize_pts(ptsi->resp, ch->pts.resp); |
| 377 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 378 | usb_buf_upd_len_and_submit(msg); |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 379 | } |
| 380 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 381 | /* Update the ISO 7816-3 TPDU receiver state */ |
| 382 | static void card_set_state(struct card_handle *ch, |
| 383 | enum iso7816_3_card_state new_state) |
| 384 | { |
Harald Welte | 903d63a | 2016-03-20 13:38:39 +0100 | [diff] [blame] | 385 | if (ch->state == new_state) |
| 386 | return; |
| 387 | |
Kévin Redon | 2fdcf3b | 2018-10-15 19:43:15 +0200 | [diff] [blame] | 388 | TRACE_DEBUG("%u: 7816 card state %s -> %s\r\n", ch->num, |
| 389 | get_value_string(iso7816_3_card_state_names, ch->state), |
| 390 | get_value_string(iso7816_3_card_state_names, new_state)); |
Harald Welte | 903d63a | 2016-03-20 13:38:39 +0100 | [diff] [blame] | 391 | ch->state = new_state; |
| 392 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 393 | switch (new_state) { |
| 394 | case ISO_S_WAIT_POWER: |
| 395 | case ISO_S_WAIT_CLK: |
| 396 | case ISO_S_WAIT_RST: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 397 | card_emu_uart_enable(ch->uart_chan, 0); // disable Rx and Tx of UART |
| 398 | card_emu_uart_update_wt(ch->uart_chan, 0); // disable timeout |
| 399 | if (ISO_S_WAIT_POWER == new_state) { |
| 400 | card_emu_uart_io_set(ch->uart_chan, false); // pull I/O line low |
| 401 | } else { |
| 402 | card_emu_uart_io_set(ch->uart_chan, true); // pull I/O line high |
| 403 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 404 | break; |
| 405 | case ISO_S_WAIT_ATR: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 406 | // reset the ETU-related values |
| 407 | ch->f = ISO7816_3_DEFAULT_FD; |
| 408 | ch->d = ISO7816_3_DEFAULT_DD; |
| 409 | card_emu_uart_update_fd(ch->uart_chan, ch->f, ch->d); // set baud rate |
| 410 | |
| 411 | // reset values optionally specified in the ATR |
| 412 | ch->fi = ISO7816_3_DEFAULT_FI; |
| 413 | ch->di = ISO7816_3_DEFAULT_DI; |
| 414 | ch->wi = ISO7816_3_DEFAULT_WI; |
| 415 | int32_t wt = iso7816_3_calculate_wt(ch->wi, ch->fi, ch->di, ch->f, ch->d); // get default waiting time |
| 416 | if (wt <= 0) { |
| 417 | TRACE_FATAL("%u: invalid WT %ld\r\n", ch->num, wt); |
| 418 | } |
| 419 | ch->wt = wt; |
| 420 | card_emu_uart_enable(ch->uart_chan, ENABLE_TX); // enable TX to be able to use the timeout |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 421 | /* the ATR should only be sent 400 to 40k clock cycles after the RESET. |
| 422 | * we use the tc_etu mechanism to wait this time. |
| 423 | * since the initial ETU is Fd=372/Dd=1 clock cycles long, we have to wait 2-107 ETU. |
| 424 | */ |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 425 | card_emu_uart_update_wt(ch->uart_chan, 2); |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 426 | break; |
| 427 | case ISO_S_IN_ATR: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 428 | // FIXME disable timeout while sending ATR |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 429 | /* Set ATR sub-state to initial state */ |
| 430 | ch->atr.idx = 0; |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 431 | /* enable USART transmission to reader */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 432 | card_emu_uart_enable(ch->uart_chan, ENABLE_TX); |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 433 | /* trigger USART TX IRQ to sent first ATR byte TS */ |
| 434 | card_emu_uart_interrupt(ch->uart_chan); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 435 | break; |
| 436 | case ISO_S_WAIT_TPDU: |
| 437 | /* enable the receiver, disable transmitter */ |
Harald Welte | 2935b3c | 2015-11-14 20:00:14 +0100 | [diff] [blame] | 438 | set_tpdu_state(ch, TPDU_S_WAIT_CLA); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 439 | card_emu_uart_enable(ch->uart_chan, ENABLE_RX); |
| 440 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 441 | case ISO_S_IN_PTS: |
| 442 | case ISO_S_IN_TPDU: |
| 443 | /* do nothing */ |
| 444 | break; |
| 445 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 446 | } |
| 447 | |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 448 | /********************************************************************** |
| 449 | * ATR handling |
| 450 | **********************************************************************/ |
| 451 | |
| 452 | /*! Transmit ATR data to reader |
| 453 | * @param[in] ch card interface connected to reader |
| 454 | * @return numbers of bytes transmitted |
| 455 | */ |
| 456 | static int tx_byte_atr(struct card_handle *ch) |
| 457 | { |
| 458 | if (NULL == ch) { |
| 459 | TRACE_ERROR("ATR TX: no card handle provided\n\r"); |
| 460 | return 0; |
| 461 | } |
| 462 | if (ISO_S_IN_ATR != ch->state) { |
| 463 | TRACE_ERROR("%u: ATR TX: no in ATR state\n\r", ch->num); |
| 464 | return 0; |
| 465 | } |
| 466 | |
| 467 | /* Transmit ATR */ |
| 468 | if (ch->atr.idx < ch->atr.len) { |
| 469 | uint8_t byte = ch->atr.atr[ch->atr.idx++]; |
| 470 | card_emu_uart_tx(ch->uart_chan, byte); |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 471 | return 1; |
| 472 | } else { /* The ATR has been completely transmitted */ |
Kévin Redon | 8b8e58b | 2018-07-30 18:19:11 +0200 | [diff] [blame] | 473 | /* search for TC2 to updated WI */ |
Kévin Redon | 8b8e58b | 2018-07-30 18:19:11 +0200 | [diff] [blame] | 474 | if (ch->atr.len >= 2 && ch->atr.atr[1] & 0xf0) { /* Y1 has some data */ |
| 475 | uint8_t atr_td1 = 2; |
| 476 | if (ch->atr.atr[1] & 0x10) { /* TA1 is present */ |
| 477 | atr_td1++; |
| 478 | } |
| 479 | if (ch->atr.atr[1] & 0x20) { /* TB1 is present */ |
| 480 | atr_td1++; |
| 481 | } |
| 482 | if (ch->atr.atr[1] & 0x40) { /* TC1 is present */ |
| 483 | atr_td1++; |
| 484 | } |
| 485 | if (ch->atr.atr[1] & 0x80) { /* TD1 is present */ |
| 486 | if (ch->atr.len > atr_td1 && ch->atr.atr[atr_td1] & 0xf0) { /* Y2 has some data */ |
| 487 | uint8_t atr_tc2 = atr_td1+1; |
| 488 | if (ch->atr.atr[atr_td1] & 0x10) { /* TA2 is present */ |
| 489 | atr_tc2++; |
| 490 | } |
| 491 | if (ch->atr.atr[atr_td1] & 0x20) { /* TB2 is present */ |
| 492 | atr_tc2++; |
| 493 | } |
| 494 | if (ch->atr.atr[atr_td1] & 0x40) { /* TC2 is present */ |
| 495 | if (ch->atr.len > atr_tc2 && ch->atr.atr[atr_tc2]) { /* TC2 encodes WI */ |
| 496 | ch->wi = ch->atr.atr[atr_tc2]; /* set WI */ |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | } |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 502 | /* FIXME update waiting time in case of card is specific mode */ |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 503 | /* reset PTS to initial state */ |
| 504 | set_pts_state(ch, PTS_S_WAIT_REQ_PTSS); |
| 505 | /* go to next state */ |
| 506 | card_set_state(ch, ISO_S_WAIT_TPDU); |
| 507 | return 0; |
| 508 | } |
| 509 | |
| 510 | /* return number of bytes transmitted */ |
| 511 | return 1; |
| 512 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 513 | |
| 514 | /********************************************************************** |
| 515 | * PTS / PPS handling |
| 516 | **********************************************************************/ |
| 517 | |
Kévin Redon | 680bdab | 2018-07-18 14:00:57 +0200 | [diff] [blame] | 518 | /* Update the PTS sub-state */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 519 | static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss) |
| 520 | { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 521 | TRACE_DEBUG("%u: 7816 PTS state %u -> %u\r\n", |
| 522 | ch->num, ch->pts.state, new_ptss); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 523 | ch->pts.state = new_ptss; |
| 524 | } |
| 525 | |
| 526 | /* Determine the next PTS state */ |
| 527 | static enum pts_state next_pts_state(struct card_handle *ch) |
| 528 | { |
| 529 | uint8_t is_resp = ch->pts.state & 0x10; |
| 530 | uint8_t sstate = ch->pts.state & 0x0f; |
| 531 | uint8_t *pts_ptr; |
| 532 | |
| 533 | if (!is_resp) |
| 534 | pts_ptr = ch->pts.req; |
| 535 | else |
| 536 | pts_ptr = ch->pts.resp; |
| 537 | |
| 538 | switch (sstate) { |
| 539 | case PTS_S_WAIT_REQ_PTSS: |
| 540 | goto from_ptss; |
| 541 | case PTS_S_WAIT_REQ_PTS0: |
| 542 | goto from_pts0; |
| 543 | case PTS_S_WAIT_REQ_PTS1: |
| 544 | goto from_pts1; |
| 545 | case PTS_S_WAIT_REQ_PTS2: |
| 546 | goto from_pts2; |
| 547 | case PTS_S_WAIT_REQ_PTS3: |
| 548 | goto from_pts3; |
| 549 | } |
| 550 | |
| 551 | if (ch->pts.state == PTS_S_WAIT_REQ_PCK) |
| 552 | return PTS_S_WAIT_RESP_PTSS; |
| 553 | |
| 554 | from_ptss: |
| 555 | return PTS_S_WAIT_REQ_PTS0 | is_resp; |
| 556 | from_pts0: |
| 557 | if (pts_ptr[_PTS0] & (1 << 4)) |
| 558 | return PTS_S_WAIT_REQ_PTS1 | is_resp; |
| 559 | from_pts1: |
| 560 | if (pts_ptr[_PTS0] & (1 << 5)) |
| 561 | return PTS_S_WAIT_REQ_PTS2 | is_resp; |
| 562 | from_pts2: |
| 563 | if (pts_ptr[_PTS0] & (1 << 6)) |
| 564 | return PTS_S_WAIT_REQ_PTS3 | is_resp; |
| 565 | from_pts3: |
| 566 | return PTS_S_WAIT_REQ_PCK | is_resp; |
| 567 | } |
| 568 | |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 569 | /*! process incoming PTS byte |
| 570 | * @param[in] ch card handle on which the byte has been received |
| 571 | * @param[in] byte received PTS byte |
| 572 | * @return new iso7816_3_card_state or -1 at the end of PTS request |
| 573 | */ |
| 574 | static int process_byte_pts(struct card_handle *ch, uint8_t byte) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 575 | { |
| 576 | switch (ch->pts.state) { |
| 577 | case PTS_S_WAIT_REQ_PTSS: |
| 578 | ch->pts.req[_PTSS] = byte; |
| 579 | break; |
| 580 | case PTS_S_WAIT_REQ_PTS0: |
| 581 | ch->pts.req[_PTS0] = byte; |
| 582 | break; |
| 583 | case PTS_S_WAIT_REQ_PTS1: |
| 584 | ch->pts.req[_PTS1] = byte; |
| 585 | break; |
| 586 | case PTS_S_WAIT_REQ_PTS2: |
| 587 | ch->pts.req[_PTS2] = byte; |
| 588 | break; |
| 589 | case PTS_S_WAIT_REQ_PTS3: |
| 590 | ch->pts.req[_PTS3] = byte; |
| 591 | break; |
| 592 | case PTS_S_WAIT_REQ_PCK: |
| 593 | ch->pts.req[_PCK] = byte; |
Harald Welte | 17db2f1 | 2016-02-26 09:48:57 +0100 | [diff] [blame] | 594 | if (ch->pts.req[_PCK] != csum_pts(ch->pts.req)) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 595 | TRACE_ERROR("%u: Error in PTS Checksum!\r\n", |
| 596 | ch->num); |
Harald Welte | 17db2f1 | 2016-02-26 09:48:57 +0100 | [diff] [blame] | 597 | /* Wait for the next TPDU */ |
| 598 | set_pts_state(ch, PTS_S_WAIT_REQ_PTSS); |
| 599 | return ISO_S_WAIT_TPDU; |
| 600 | } |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 601 | /* FIXME check if proposal matches capabilities in TA1 */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 602 | memcpy(ch->pts.resp, ch->pts.req, sizeof(ch->pts.resp)); |
| 603 | break; |
Harald Welte | 4c473da | 2015-11-14 13:31:11 +0100 | [diff] [blame] | 604 | default: |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 605 | TRACE_ERROR("%u: process_byte_pts() in invalid state %u\r\n", |
| 606 | ch->num, ch->pts.state); |
Harald Welte | 4c473da | 2015-11-14 13:31:11 +0100 | [diff] [blame] | 607 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 608 | } |
| 609 | /* calculate the next state and set it */ |
| 610 | set_pts_state(ch, next_pts_state(ch)); |
| 611 | |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 612 | if (ch->pts.state == PTS_S_WAIT_RESP_PTSS) { |
| 613 | flush_pts(ch); |
| 614 | /* activate UART TX to transmit PTS response */ |
| 615 | card_emu_uart_enable(ch->uart_chan, ENABLE_TX); |
Harald Welte | 53079bb | 2016-03-20 14:58:35 +0100 | [diff] [blame] | 616 | /* don't fall-through to the 'return ISO_S_IN_PTS' |
| 617 | * below, rather keep ISO7816 state as-is, it will be |
| 618 | * further updated by the tx-completion handler */ |
Harald Welte | ccb8a22 | 2016-03-20 13:37:11 +0100 | [diff] [blame] | 619 | return -1; |
Harald Welte | 4ba66d0 | 2016-02-25 19:38:56 +0100 | [diff] [blame] | 620 | } |
| 621 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 622 | return ISO_S_IN_PTS; |
| 623 | } |
| 624 | |
| 625 | /* return a single byte to be transmitted to the reader */ |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 626 | static int tx_byte_pts(struct card_handle *ch) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 627 | { |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 628 | uint8_t byte; |
| 629 | |
| 630 | /* 1: Determine the next transmit byte */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 631 | switch (ch->pts.state) { |
| 632 | case PTS_S_WAIT_RESP_PTSS: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 633 | byte = ch->pts.resp[_PTSS]; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 634 | break; |
| 635 | case PTS_S_WAIT_RESP_PTS0: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 636 | byte = ch->pts.resp[_PTS0]; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 637 | break; |
| 638 | case PTS_S_WAIT_RESP_PTS1: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 639 | byte = ch->pts.resp[_PTS1]; |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 640 | // TODO the value should have been validated when receiving the request |
| 641 | ch->f = iso7816_3_fi_table[byte >> 4]; // save selected Fn |
| 642 | if (0 == ch->f) { |
| 643 | TRACE_ERROR("%u: invalid F index in PPS response: %u\r\n", ch->num, byte >> 4); |
| 644 | // TODO become unresponsive to signal error condition |
| 645 | } |
| 646 | ch->d = iso7816_3_di_table[byte & 0xf]; // save selected Dn |
| 647 | if (0 == ch->d) { |
| 648 | TRACE_ERROR("%u: invalid D index in PPS response: %u\r\n", ch->num, byte & 0xf); |
| 649 | // TODO become unresponsive to signal error condition |
| 650 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 651 | break; |
| 652 | case PTS_S_WAIT_RESP_PTS2: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 653 | byte = ch->pts.resp[_PTS2]; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 654 | break; |
| 655 | case PTS_S_WAIT_RESP_PTS3: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 656 | byte = ch->pts.resp[_PTS3]; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 657 | break; |
| 658 | case PTS_S_WAIT_RESP_PCK: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 659 | byte = ch->pts.resp[_PCK]; |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 660 | break; |
Harald Welte | d79dc4f | 2015-11-14 13:32:21 +0100 | [diff] [blame] | 661 | default: |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 662 | TRACE_ERROR("%u: get_byte_pts() in invalid state %u\r\n", |
| 663 | ch->num, ch->pts.state); |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 664 | return 0; |
| 665 | } |
| 666 | |
| 667 | /* 2: Transmit the byte */ |
| 668 | card_emu_uart_tx(ch->uart_chan, byte); |
| 669 | |
| 670 | /* 3: Update the state */ |
| 671 | |
| 672 | switch (ch->pts.state) { |
| 673 | case PTS_S_WAIT_RESP_PCK: |
Harald Welte | c58bba0 | 2016-03-20 14:57:53 +0100 | [diff] [blame] | 674 | card_emu_uart_wait_tx_idle(ch->uart_chan); |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 675 | card_emu_uart_update_fd(ch->uart_chan, ch->f, ch->d); // set selected baud rate |
| 676 | int32_t wt = iso7816_3_calculate_wt(ch->wi, ch->fi, ch->di, ch->f, ch->d); // get new waiting time |
| 677 | if (wt <= 0) { |
| 678 | TRACE_ERROR("%u: invalid WT calculated: %ld\r\n", ch->num, wt); |
| 679 | // TODO become unresponsive to signal error condition |
| 680 | } else { |
| 681 | ch->wt = wt; |
| 682 | } |
| 683 | // FIXME disable WT |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 684 | /* Wait for the next TPDU */ |
| 685 | card_set_state(ch, ISO_S_WAIT_TPDU); |
| 686 | set_pts_state(ch, PTS_S_WAIT_REQ_PTSS); |
| 687 | break; |
| 688 | default: |
| 689 | /* calculate the next state and set it */ |
| 690 | set_pts_state(ch, next_pts_state(ch)); |
Harald Welte | d79dc4f | 2015-11-14 13:32:21 +0100 | [diff] [blame] | 691 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 692 | } |
Harald Welte | d79dc4f | 2015-11-14 13:32:21 +0100 | [diff] [blame] | 693 | |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 694 | /* return number of bytes transmitted */ |
| 695 | return 1; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 696 | } |
| 697 | |
| 698 | |
| 699 | /********************************************************************** |
| 700 | * TPDU handling |
| 701 | **********************************************************************/ |
| 702 | |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 703 | |
| 704 | /* compute number of data bytes according to Chapter 10.3.2 of 7816-3 */ |
| 705 | static unsigned int t0_num_data_bytes(uint8_t p3, int reader_to_card) |
| 706 | { |
| 707 | if (reader_to_card) { |
| 708 | return p3; |
| 709 | } else { |
| 710 | if (p3 == 0) |
| 711 | return 256; |
| 712 | else |
| 713 | return p3; |
| 714 | } |
| 715 | } |
| 716 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 717 | /* add a just-received TPDU byte (from reader) to USB buffer */ |
Harald Welte | 61bb30e | 2015-11-14 23:44:14 +0100 | [diff] [blame] | 718 | static void add_tpdu_byte(struct card_handle *ch, uint8_t byte) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 719 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 720 | struct msgb *msg; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 721 | struct cardemu_usb_msg_rx_data *rd; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 722 | unsigned int num_data_bytes = t0_num_data_bytes(ch->tpdu.hdr[_P3], 0); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 723 | |
| 724 | /* ensure we have a buffer */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 725 | if (!ch->uart_rx_msg) { |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 726 | msg = ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, |
| 727 | SIMTRACE_MSGT_DO_CEMU_RX_DATA); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 728 | if (!ch->uart_rx_msg) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 729 | TRACE_ERROR("%u: Received UART byte but ENOMEM\r\n", |
| 730 | ch->num); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 731 | return; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 732 | } |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 733 | msgb_put(msg, sizeof(*rd)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 734 | } else |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 735 | msg = ch->uart_rx_msg; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 736 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 737 | rd = (struct cardemu_usb_msg_rx_data *) msg->l2h; |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 738 | msgb_put_u8(msg, byte); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 739 | |
| 740 | /* check if the buffer is full. If so, send it */ |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 741 | if (msgb_l2len(msg) >= sizeof(*rd) + num_data_bytes) { |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 742 | rd->flags |= CEMU_DATA_F_FINAL; |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 743 | flush_rx_buffer(ch); |
Harald Welte | 61bb30e | 2015-11-14 23:44:14 +0100 | [diff] [blame] | 744 | /* We need to transmit the SW now, */ |
| 745 | set_tpdu_state(ch, TPDU_S_WAIT_TX); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 746 | } else if (msgb_tailroom(msg) <= 0) |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 747 | flush_rx_buffer(ch); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 748 | } |
| 749 | |
| 750 | static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts) |
| 751 | { |
Harald Welte | 05b41c6 | 2015-11-14 20:58:48 +0100 | [diff] [blame] | 752 | if (ch->tpdu.state == new_ts) |
| 753 | return; |
Kévin Redon | 6b0afb3 | 2019-09-24 17:44:59 +0200 | [diff] [blame] | 754 | if (ISO_S_IN_TPDU != ch->state && ISO_S_WAIT_TPDU != ch->state) { |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 755 | TRACE_ERROR("%u: setting TPDU state in %s state\r\n", ch->num, |
| 756 | get_value_string(iso7816_3_card_state_names, ch->state)); |
| 757 | } |
Harald Welte | 05b41c6 | 2015-11-14 20:58:48 +0100 | [diff] [blame] | 758 | |
Kévin Redon | 2fdcf3b | 2018-10-15 19:43:15 +0200 | [diff] [blame] | 759 | TRACE_DEBUG("%u: 7816 TPDU state %s -> %s\r\n", ch->num, |
| 760 | get_value_string(tpdu_state_names, ch->tpdu.state), |
| 761 | get_value_string(tpdu_state_names, new_ts)); |
Harald Welte | 903d63a | 2016-03-20 13:38:39 +0100 | [diff] [blame] | 762 | ch->tpdu.state = new_ts; |
| 763 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 764 | switch (new_ts) { |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 765 | case TPDU_S_WAIT_CLA: // we will be waiting for the next incoming TDPU |
| 766 | card_emu_uart_enable(ch->uart_chan, ENABLE_RX); // switch back to receiving mode |
| 767 | card_emu_uart_update_wt(ch->uart_chan, 0); // disable waiting time since we don't expect any data |
| 768 | break; |
| 769 | case TPDU_S_WAIT_INS: // the reader started sending the TPDU header |
| 770 | card_emu_uart_update_wt(ch->uart_chan, ch->wt); // start waiting for the rest of the header/body |
| 771 | break; |
| 772 | case TPDU_S_WAIT_RX: // the reader should send us the TPDU body data |
| 773 | card_emu_uart_enable(ch->uart_chan, ENABLE_RX); // switch to receive mode to receive the body |
| 774 | card_emu_uart_update_wt(ch->uart_chan, ch->wt); // start waiting for the rest body |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 775 | break; |
| 776 | case TPDU_S_WAIT_PB: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 777 | card_emu_uart_enable(ch->uart_chan, ENABLE_TX); // header is completely received, now we need to transmit the procedure byte |
| 778 | card_emu_uart_update_wt(ch->uart_chan, ch->wt); // prepare to extend the waiting time once half of it is reached |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 779 | break; |
Harald Welte | ad43440 | 2016-03-02 11:18:29 +0100 | [diff] [blame] | 780 | default: |
| 781 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 782 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 783 | } |
| 784 | |
| 785 | static enum tpdu_state next_tpdu_state(struct card_handle *ch) |
| 786 | { |
| 787 | switch (ch->tpdu.state) { |
| 788 | case TPDU_S_WAIT_CLA: |
| 789 | return TPDU_S_WAIT_INS; |
| 790 | case TPDU_S_WAIT_INS: |
| 791 | return TPDU_S_WAIT_P1; |
| 792 | case TPDU_S_WAIT_P1: |
| 793 | return TPDU_S_WAIT_P2; |
| 794 | case TPDU_S_WAIT_P2: |
| 795 | return TPDU_S_WAIT_P3; |
| 796 | case TPDU_S_WAIT_P3: |
| 797 | return TPDU_S_WAIT_PB; |
| 798 | /* simply stay in Rx or Tx by default */ |
| 799 | case TPDU_S_WAIT_PB: |
| 800 | return TPDU_S_WAIT_PB; |
| 801 | case TPDU_S_WAIT_RX: |
| 802 | return TPDU_S_WAIT_RX; |
| 803 | case TPDU_S_WAIT_TX: |
| 804 | return TPDU_S_WAIT_TX; |
| 805 | } |
Harald Welte | 4c473da | 2015-11-14 13:31:11 +0100 | [diff] [blame] | 806 | /* we should never reach here */ |
| 807 | assert(0); |
| 808 | return -1; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 809 | } |
| 810 | |
| 811 | static void send_tpdu_header(struct card_handle *ch) |
| 812 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 813 | struct msgb *msg; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 814 | struct cardemu_usb_msg_rx_data *rd; |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 815 | uint8_t *cur; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 816 | |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 817 | TRACE_INFO("%u: %s: %02x %02x %02x %02x %02x\r\n", |
| 818 | ch->num, __func__, |
Harald Welte | 43f7949 | 2016-02-29 10:06:54 +0100 | [diff] [blame] | 819 | ch->tpdu.hdr[0], ch->tpdu.hdr[1], |
| 820 | ch->tpdu.hdr[2], ch->tpdu.hdr[3], |
| 821 | ch->tpdu.hdr[4]); |
| 822 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 823 | /* if we already/still have a context, send it off */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 824 | if (ch->uart_rx_msg) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 825 | TRACE_DEBUG("%u: have old buffer\r\n", ch->num); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 826 | if (msgb_l2len(ch->uart_rx_msg)) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 827 | TRACE_DEBUG("%u: flushing old buffer\r\n", ch->num); |
Harald Welte | f1697e2 | 2016-03-02 10:28:54 +0100 | [diff] [blame] | 828 | flush_rx_buffer(ch); |
| 829 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 830 | } |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 831 | TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num); |
| 832 | /* ensure we have a new buffer */ |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 833 | ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, |
| 834 | SIMTRACE_MSGT_DO_CEMU_RX_DATA); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 835 | if (!ch->uart_rx_msg) { |
| 836 | TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__); |
| 837 | return; |
| 838 | } |
| 839 | msg = ch->uart_rx_msg; |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 840 | rd = (struct cardemu_usb_msg_rx_data *) msgb_put(msg, sizeof(*rd)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 841 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 842 | /* initialize header */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 843 | rd->flags = CEMU_DATA_F_TPDU_HDR; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 844 | |
| 845 | /* copy TPDU header to data field */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 846 | cur = msgb_put(msg, sizeof(ch->tpdu.hdr)); |
| 847 | memcpy(cur, ch->tpdu.hdr, sizeof(ch->tpdu.hdr)); |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 848 | /* rd->data_len is set in flush_rx_buffer() */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 849 | |
Harald Welte | b5288e8 | 2015-11-14 21:15:52 +0100 | [diff] [blame] | 850 | flush_rx_buffer(ch); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 851 | } |
| 852 | |
| 853 | static enum iso7816_3_card_state |
| 854 | process_byte_tpdu(struct card_handle *ch, uint8_t byte) |
| 855 | { |
| 856 | switch (ch->tpdu.state) { |
| 857 | case TPDU_S_WAIT_CLA: |
| 858 | ch->tpdu.hdr[_CLA] = byte; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 859 | set_tpdu_state(ch, next_tpdu_state(ch)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 860 | break; |
| 861 | case TPDU_S_WAIT_INS: |
| 862 | ch->tpdu.hdr[_INS] = byte; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 863 | set_tpdu_state(ch, next_tpdu_state(ch)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 864 | break; |
| 865 | case TPDU_S_WAIT_P1: |
| 866 | ch->tpdu.hdr[_P1] = byte; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 867 | set_tpdu_state(ch, next_tpdu_state(ch)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 868 | break; |
| 869 | case TPDU_S_WAIT_P2: |
| 870 | ch->tpdu.hdr[_P2] = byte; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 871 | set_tpdu_state(ch, next_tpdu_state(ch)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 872 | break; |
| 873 | case TPDU_S_WAIT_P3: |
| 874 | ch->tpdu.hdr[_P3] = byte; |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 875 | set_tpdu_state(ch, next_tpdu_state(ch)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 876 | /* FIXME: start timer to transmit further 0x60 */ |
| 877 | /* send the TPDU header as part of a procedure byte |
| 878 | * request to the USB host */ |
| 879 | send_tpdu_header(ch); |
| 880 | break; |
| 881 | case TPDU_S_WAIT_RX: |
Harald Welte | 61bb30e | 2015-11-14 23:44:14 +0100 | [diff] [blame] | 882 | add_tpdu_byte(ch, byte); |
| 883 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 884 | default: |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 885 | TRACE_ERROR("%u: process_byte_tpdu() in invalid state %u\r\n", |
| 886 | ch->num, ch->tpdu.state); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 887 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 888 | |
| 889 | /* ensure we stay in TPDU ISO state */ |
| 890 | return ISO_S_IN_TPDU; |
| 891 | } |
| 892 | |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 893 | /* tx a single byte to be transmitted to the reader */ |
| 894 | static int tx_byte_tpdu(struct card_handle *ch) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 895 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 896 | struct msgb *msg; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 897 | struct cardemu_usb_msg_tx_data *td; |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 898 | uint8_t byte; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 899 | |
| 900 | /* ensure we are aware of any data that might be pending for |
| 901 | * transmit */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 902 | if (!ch->uart_tx_msg) { |
Harald Welte | 53079bb | 2016-03-20 14:58:35 +0100 | [diff] [blame] | 903 | /* uart_tx_queue is filled from main loop, so no need |
| 904 | * for irq-safe operations */ |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 905 | if (llist_empty(&ch->uart_tx_queue)) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 906 | return 0; |
| 907 | |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 908 | /* dequeue first at head */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 909 | ch->uart_tx_msg = msgb_dequeue(&ch->uart_tx_queue); |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 910 | ch->uart_tx_msg->l1h = ch->uart_tx_msg->head; |
| 911 | ch->uart_tx_msg->l2h = ch->uart_tx_msg->l1h + sizeof(struct simtrace_msg_hdr); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 912 | msg = ch->uart_tx_msg; |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 913 | /* remove the header */ |
| 914 | msgb_pull(msg, sizeof(struct simtrace_msg_hdr) + sizeof(*td)); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 915 | } |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 916 | msg = ch->uart_tx_msg; |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 917 | td = (struct cardemu_usb_msg_tx_data *) msg->l2h; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 918 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 919 | /* take the next pending byte out of the msgb */ |
| 920 | byte = msgb_pull_u8(msg); |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 921 | |
| 922 | card_emu_uart_tx(ch->uart_chan, byte); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 923 | |
Kévin Redon | 0f4abf5 | 2018-08-02 17:42:48 +0200 | [diff] [blame] | 924 | /* this must happen _after_ the byte has been transmitted */ |
Harald Welte | f16b618 | 2016-02-24 22:18:46 +0100 | [diff] [blame] | 925 | switch (ch->tpdu.state) { |
| 926 | case TPDU_S_WAIT_PB: |
| 927 | /* if we just transmitted the procedure byte, we need to decide |
| 928 | * if we want to continue to receive or transmit */ |
| 929 | if (td->flags & CEMU_DATA_F_PB_AND_TX) |
| 930 | set_tpdu_state(ch, TPDU_S_WAIT_TX); |
| 931 | else if (td->flags & CEMU_DATA_F_PB_AND_RX) |
| 932 | set_tpdu_state(ch, TPDU_S_WAIT_RX); |
| 933 | break; |
Harald Welte | ad43440 | 2016-03-02 11:18:29 +0100 | [diff] [blame] | 934 | default: |
| 935 | break; |
Harald Welte | f16b618 | 2016-02-24 22:18:46 +0100 | [diff] [blame] | 936 | } |
| 937 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 938 | /* check if the buffer has now been fully transmitted */ |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 939 | if (msgb_length(msg) == 0) { |
Harald Welte | 52922ff | 2015-11-14 20:59:56 +0100 | [diff] [blame] | 940 | if (td->flags & CEMU_DATA_F_PB_AND_RX) { |
| 941 | /* we have just sent the procedure byte and now |
| 942 | * need to continue receiving */ |
| 943 | set_tpdu_state(ch, TPDU_S_WAIT_RX); |
Harald Welte | 2935b3c | 2015-11-14 20:00:14 +0100 | [diff] [blame] | 944 | } else { |
Harald Welte | 52922ff | 2015-11-14 20:59:56 +0100 | [diff] [blame] | 945 | /* we have transmitted all bytes */ |
| 946 | if (td->flags & CEMU_DATA_F_FINAL) { |
| 947 | /* this was the final part of the APDU, go |
Harald Welte | 53079bb | 2016-03-20 14:58:35 +0100 | [diff] [blame] | 948 | * back to state one */ |
Harald Welte | 52922ff | 2015-11-14 20:59:56 +0100 | [diff] [blame] | 949 | card_set_state(ch, ISO_S_WAIT_TPDU); |
Harald Welte | 52922ff | 2015-11-14 20:59:56 +0100 | [diff] [blame] | 950 | } |
Harald Welte | 2935b3c | 2015-11-14 20:00:14 +0100 | [diff] [blame] | 951 | } |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 952 | usb_buf_free(msg); |
| 953 | ch->uart_tx_msg = NULL; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 954 | } |
| 955 | |
| 956 | return 1; |
| 957 | } |
| 958 | |
| 959 | /********************************************************************** |
| 960 | * Public API |
| 961 | **********************************************************************/ |
| 962 | |
| 963 | /* process a single byte received from the reader */ |
| 964 | void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte) |
| 965 | { |
| 966 | int new_state = -1; |
| 967 | |
| 968 | ch->stats.rx_bytes++; |
| 969 | |
| 970 | switch (ch->state) { |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 971 | case ISO_S_WAIT_TPDU: |
| 972 | if (byte == 0xff) { |
| 973 | new_state = process_byte_pts(ch, byte); |
| 974 | ch->stats.pps++; |
| 975 | goto out_silent; |
| 976 | } |
| 977 | /* fall-through */ |
| 978 | case ISO_S_IN_TPDU: |
| 979 | new_state = process_byte_tpdu(ch, byte); |
| 980 | break; |
| 981 | case ISO_S_IN_PTS: |
| 982 | new_state = process_byte_pts(ch, byte); |
| 983 | goto out_silent; |
Kévin Redon | 5f6b871 | 2018-07-11 09:49:06 +0200 | [diff] [blame] | 984 | default: |
| 985 | TRACE_ERROR("%u: Received UART char in invalid 7816 state " |
| 986 | "%u\r\n", ch->num, ch->state); |
| 987 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 988 | } |
| 989 | |
| 990 | out_silent: |
| 991 | if (new_state != -1) |
| 992 | card_set_state(ch, new_state); |
| 993 | } |
| 994 | |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 995 | /* transmit a single byte to the reader */ |
| 996 | int card_emu_tx_byte(struct card_handle *ch) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 997 | { |
| 998 | int rc = 0; |
| 999 | |
| 1000 | switch (ch->state) { |
| 1001 | case ISO_S_IN_ATR: |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1002 | rc = tx_byte_atr(ch); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1003 | break; |
| 1004 | case ISO_S_IN_PTS: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 1005 | rc = tx_byte_pts(ch); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1006 | break; |
| 1007 | case ISO_S_IN_TPDU: |
Harald Welte | 855ba9e | 2016-02-24 21:00:46 +0100 | [diff] [blame] | 1008 | rc = tx_byte_tpdu(ch); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1009 | break; |
Harald Welte | ad43440 | 2016-03-02 11:18:29 +0100 | [diff] [blame] | 1010 | default: |
| 1011 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1012 | } |
| 1013 | |
| 1014 | if (rc) |
| 1015 | ch->stats.tx_bytes++; |
| 1016 | |
Harald Welte | 4d80467 | 2015-11-14 23:02:38 +0100 | [diff] [blame] | 1017 | /* if we return 0 here, the UART needs to disable transmit-ready |
| 1018 | * interrupts */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1019 | return rc; |
| 1020 | } |
| 1021 | |
Harald Welte | acae412 | 2016-03-02 10:27:58 +0100 | [diff] [blame] | 1022 | void card_emu_have_new_uart_tx(struct card_handle *ch) |
| 1023 | { |
| 1024 | switch (ch->state) { |
| 1025 | case ISO_S_IN_TPDU: |
| 1026 | switch (ch->tpdu.state) { |
| 1027 | case TPDU_S_WAIT_TX: |
| 1028 | case TPDU_S_WAIT_PB: |
| 1029 | card_emu_uart_enable(ch->uart_chan, ENABLE_TX); |
| 1030 | break; |
| 1031 | default: |
| 1032 | break; |
| 1033 | } |
| 1034 | default: |
| 1035 | break; |
| 1036 | } |
| 1037 | } |
| 1038 | |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1039 | void card_emu_report_status(struct card_handle *ch) |
| 1040 | { |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 1041 | struct msgb *msg; |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1042 | struct cardemu_usb_msg_status *sts; |
| 1043 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 1044 | msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, |
| 1045 | SIMTRACE_MSGT_BD_CEMU_STATUS); |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 1046 | if (!msg) |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1047 | return; |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 1048 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 1049 | sts = (struct cardemu_usb_msg_status *) msgb_put(msg, sizeof(*sts)); |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1050 | sts->flags = 0; |
| 1051 | if (ch->vcc_active) |
| 1052 | sts->flags |= CEMU_STATUS_F_VCC_PRESENT; |
| 1053 | if (ch->clocked) |
| 1054 | sts->flags |= CEMU_STATUS_F_CLK_ACTIVE; |
| 1055 | if (ch->in_reset) |
| 1056 | sts->flags |= CEMU_STATUS_F_RESET_ACTIVE; |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1057 | /* FIXME set voltage and card insert values */ |
| 1058 | sts->f = ch->f; |
| 1059 | sts->d = ch->d; |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1060 | sts->wi = ch->wi; |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1061 | sts->wt = ch->wt; |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1062 | |
Harald Welte | 25a9a80 | 2017-05-08 13:30:09 +0200 | [diff] [blame] | 1063 | usb_buf_upd_len_and_submit(msg); |
Harald Welte | ff16065 | 2016-03-19 21:59:06 +0100 | [diff] [blame] | 1064 | } |
| 1065 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1066 | /* hardware driver informs us that a card I/O signal has changed */ |
| 1067 | void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active) |
| 1068 | { |
| 1069 | switch (io) { |
| 1070 | case CARD_IO_VCC: |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1071 | if (active == 0 && ch->vcc_active == 1) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1072 | TRACE_INFO("%u: VCC deactivated\r\n", ch->num); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1073 | card_set_state(ch, ISO_S_WAIT_POWER); |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1074 | } else if (active == 1 && ch->vcc_active == 0) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1075 | TRACE_INFO("%u: VCC activated\r\n", ch->num); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1076 | card_set_state(ch, ISO_S_WAIT_CLK); |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1077 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1078 | ch->vcc_active = active; |
| 1079 | break; |
| 1080 | case CARD_IO_CLK: |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1081 | if (active == 1 && ch->clocked == 0) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1082 | TRACE_INFO("%u: CLK activated\r\n", ch->num); |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1083 | if (ch->state == ISO_S_WAIT_CLK) |
| 1084 | card_set_state(ch, ISO_S_WAIT_RST); |
| 1085 | } else if (active == 0 && ch->clocked == 1) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1086 | TRACE_INFO("%u: CLK deactivated\r\n", ch->num); |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1087 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1088 | ch->clocked = active; |
| 1089 | break; |
| 1090 | case CARD_IO_RST: |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1091 | if (active == 0 && ch->in_reset) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1092 | TRACE_INFO("%u: RST released\r\n", ch->num); |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1093 | if (ch->vcc_active && ch->clocked && ISO_S_WAIT_RST == ch->state) { |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1094 | /* prepare to send the ATR */ |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1095 | card_set_state(ch, ISO_S_WAIT_ATR); |
Harald Welte | 47ee283 | 2016-02-29 10:09:46 +0100 | [diff] [blame] | 1096 | } |
| 1097 | } else if (active && !ch->in_reset) { |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1098 | TRACE_INFO("%u: RST asserted\r\n", ch->num); |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1099 | card_set_state(ch, ISO_S_WAIT_RST); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1100 | } |
| 1101 | ch->in_reset = active; |
| 1102 | break; |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1103 | default: |
| 1104 | break; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1105 | } |
| 1106 | } |
| 1107 | |
| 1108 | /* User sets a new ATR to be returned during next card reset */ |
| 1109 | int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len) |
| 1110 | { |
| 1111 | if (len > sizeof(ch->atr.atr)) |
| 1112 | return -1; |
| 1113 | |
Kévin Redon | b149ea3 | 2019-09-24 17:48:25 +0200 | [diff] [blame] | 1114 | /* ignore new ATR for now since we PPS has not been tested |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1115 | memcpy(ch->atr.atr, atr, len); |
| 1116 | ch->atr.len = len; |
| 1117 | ch->atr.idx = 0; |
Kévin Redon | b149ea3 | 2019-09-24 17:48:25 +0200 | [diff] [blame] | 1118 | */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1119 | |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1120 | #if TRACE_LEVEL >= TRACE_LEVEL_INFO |
| 1121 | uint8_t i; |
| 1122 | TRACE_INFO("%u: ATR set: ", ch->num); |
Kévin Redon | b149ea3 | 2019-09-24 17:48:25 +0200 | [diff] [blame] | 1123 | for (i = 0; i < len; i++) { |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1124 | TRACE_INFO_WP("%02x ", atr[i]); |
| 1125 | } |
| 1126 | TRACE_INFO_WP("\n\r"); |
Kévin Redon | b149ea3 | 2019-09-24 17:48:25 +0200 | [diff] [blame] | 1127 | TRACE_INFO("%u: ATR set currently ignored\n\r", ch->num); |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1128 | #endif |
| 1129 | /* FIXME: race condition with transmitting ATR to reader? */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1130 | |
| 1131 | return 0; |
| 1132 | } |
| 1133 | |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1134 | void card_emu_wt_halfed(struct card_handle *ch) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1135 | { |
Harald Welte | dda7355 | 2016-03-02 10:29:55 +0100 | [diff] [blame] | 1136 | switch (ch->state) { |
| 1137 | case ISO_S_IN_TPDU: |
| 1138 | switch (ch->tpdu.state) { |
Harald Welte | dda7355 | 2016-03-02 10:29:55 +0100 | [diff] [blame] | 1139 | case TPDU_S_WAIT_TX: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1140 | case TPDU_S_WAIT_PB: |
Harald Welte | dda7355 | 2016-03-02 10:29:55 +0100 | [diff] [blame] | 1141 | putchar('N'); |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1142 | card_emu_uart_tx(ch->uart_chan, ISO7816_3_PB_NULL); // we are waiting for data from the user. send a procedure byte to ask the reader to wait more time |
| 1143 | card_emu_uart_reset_wt(ch->uart_chan); // reset WT |
Harald Welte | dda7355 | 2016-03-02 10:29:55 +0100 | [diff] [blame] | 1144 | break; |
| 1145 | default: |
| 1146 | break; |
| 1147 | } |
Harald Welte | dda7355 | 2016-03-02 10:29:55 +0100 | [diff] [blame] | 1148 | default: |
| 1149 | break; |
| 1150 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1151 | } |
| 1152 | |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1153 | void card_emu_wt_expired(struct card_handle *ch) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1154 | { |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1155 | switch (ch->state) { |
| 1156 | case ISO_S_WAIT_ATR: |
| 1157 | /* ISO 7816-3 6.2.1 time tc has passed, we can now send the ATR */ |
| 1158 | card_set_state(ch, ISO_S_IN_ATR); |
| 1159 | break; |
| 1160 | default: |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1161 | // TODO become unresponsive |
Kévin Redon | ebe672e | 2018-07-29 00:18:12 +0200 | [diff] [blame] | 1162 | TRACE_ERROR("%u: wtime_exp\r\n", ch->num); |
| 1163 | break; |
| 1164 | } |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1165 | } |
| 1166 | |
Kévin Redon | 1a88fd8 | 2019-05-15 00:23:21 +0200 | [diff] [blame] | 1167 | /* shortest ATR possible (uses default speed and no options) */ |
| 1168 | static const uint8_t default_atr[] = { 0x3B, 0x00 }; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1169 | |
| 1170 | static struct card_handle card_handles[NUM_SLOTS]; |
| 1171 | |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 1172 | struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, |
| 1173 | uint8_t in_ep, uint8_t irq_ep) |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1174 | { |
| 1175 | struct card_handle *ch; |
| 1176 | |
| 1177 | if (slot_num >= ARRAY_SIZE(card_handles)) |
| 1178 | return NULL; |
| 1179 | |
| 1180 | ch = &card_handles[slot_num]; |
| 1181 | |
| 1182 | memset(ch, 0, sizeof(*ch)); |
| 1183 | |
Harald Welte | 54cb3d0 | 2016-02-29 14:12:40 +0100 | [diff] [blame] | 1184 | INIT_LLIST_HEAD(&ch->uart_tx_queue); |
| 1185 | |
Kévin Redon | 680bdab | 2018-07-18 14:00:57 +0200 | [diff] [blame] | 1186 | /* initialize the card_handle with reasonable defaults */ |
Harald Welte | dde112e | 2016-03-20 16:42:11 +0100 | [diff] [blame] | 1187 | ch->num = slot_num; |
Harald Welte | 8e7fca3 | 2017-05-07 16:14:33 +0200 | [diff] [blame] | 1188 | ch->irq_ep = irq_ep; |
| 1189 | ch->in_ep = in_ep; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1190 | ch->state = ISO_S_WAIT_POWER; |
| 1191 | ch->vcc_active = 0; |
| 1192 | ch->in_reset = 1; |
| 1193 | ch->clocked = 0; |
| 1194 | |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1195 | ch->fi = ISO7816_3_DEFAULT_FI; |
| 1196 | ch->di = ISO7816_3_DEFAULT_DI; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1197 | ch->wi = ISO7816_3_DEFAULT_WI; |
Kévin Redon | 76c2eeb | 2019-06-13 15:41:52 +0200 | [diff] [blame] | 1198 | ch->wt = ISO7816_3_DEFAULT_WT;; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1199 | |
| 1200 | ch->tc_chan = tc_chan; |
| 1201 | ch->uart_chan = uart_chan; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1202 | |
| 1203 | ch->atr.idx = 0; |
| 1204 | ch->atr.len = sizeof(default_atr); |
| 1205 | memcpy(ch->atr.atr, default_atr, ch->atr.len); |
| 1206 | |
| 1207 | ch->pts.state = PTS_S_WAIT_REQ_PTSS; |
| 1208 | ch->tpdu.state = TPDU_S_WAIT_CLA; |
| 1209 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 1210 | return ch; |
| 1211 | } |