blob: f71c7207a205b6c8d2334182746d2340697fe397 [file] [log] [blame]
Harald Welte0e968cc2020-02-22 18:16:16 +01001/* (C) 2018-2020 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Harald Welte0e968cc2020-02-22 18:16:16 +010017 */
18
19#include <stdio.h>
20#include <errno.h>
21
22#include <libusb.h>
23
24#include <osmocom/core/fsm.h>
25#include <osmocom/core/utils.h>
26
27#include <osmocom/usb/libusb.h>
28
29#include <osmocom/simtrace2/apdu_dispatch.h>
30#include <osmocom/simtrace2/simtrace2_api.h>
31#include <osmocom/simtrace2/simtrace_prot.h>
32
33#include "client.h"
34#include "debug.h"
35
Harald Welteb97e2fb2020-03-03 21:01:11 +010036#define LOGCI(ci, lvl, fmt, args ...) \
37 LOGP(DST2, lvl, fmt, ## args)
Harald Welte0e968cc2020-02-22 18:16:16 +010038
39/***********************************************************************
40 * Incoming Messages from cardem firmware
41 ***********************************************************************/
42
43/*! \brief Process a STATUS message from the SIMtrace2 */
44static int process_do_status(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len)
45{
46 struct cardemu_usb_msg_status *status;
47 status = (struct cardemu_usb_msg_status *) buf;
48
Harald Welteb97e2fb2020-03-03 21:01:11 +010049 LOGCI(ci, LOGL_INFO, "SIMtrace => STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n",
50 status->flags, status->fi, status->di, status->wi, status->waiting_time);
Harald Welte0e968cc2020-02-22 18:16:16 +010051
52 return 0;
53}
54
55/*! \brief Process a PTS indication message from the SIMtrace2 */
56static int process_do_pts(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len)
57{
58 struct cardemu_usb_msg_pts_info *pts = (struct cardemu_usb_msg_pts_info *) buf;
59 struct bankd_client *bc = ci->priv;
60 struct frontend_pts fpts = {
61 .buf = pts->req,
62 .len = sizeof(pts->req),
63 };
64
Harald Welteb97e2fb2020-03-03 21:01:11 +010065 LOGCI(ci, LOGL_INFO, "SIMtrace => PTS req: %s\n", osmo_hexdump(pts->req, sizeof(pts->req)));
Harald Welte0e968cc2020-02-22 18:16:16 +010066
67 osmo_fsm_inst_dispatch(bc->main_fi, MF_E_MDM_PTS_IND, &fpts);
68
69 return 0;
70}
71
72/*! \brief Process a ERROR indication message from the SIMtrace2 */
73__attribute__((unused)) static int process_do_error(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len)
74{
75 struct cardemu_usb_msg_error *err;
76 err = (struct cardemu_usb_msg_error *) buf;
77
Harald Welteb97e2fb2020-03-03 21:01:11 +010078 LOGCI(ci, LOGL_ERROR, "SIMtrace => ERROR: %u/%u/%u: %s\n",
79 err->severity, err->subsystem, err->code, err->msg_len ? (char *)err->msg : "");
Harald Welte0e968cc2020-02-22 18:16:16 +010080
81 return 0;
82}
83
84static struct osmo_apdu_context ac; // this will hold the complete APDU (across calls)
85
86/*! \brief Process a RX-DATA indication message from the SIMtrace2 */
87static int process_do_rx_da(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len)
88{
89 struct cardemu_usb_msg_rx_data *data = (struct cardemu_usb_msg_rx_data *) buf;
90 struct bankd_client *bc = ci->priv;
91 struct frontend_tpdu ftpdu;
92 int rc;
93
Harald Welte06fff2a2020-05-25 18:14:49 +020094 LOGCI(ci, LOGL_DEBUG, "SIMtrace => DATA: flags=%x, %s\n", data->flags,
Harald Welte0e968cc2020-02-22 18:16:16 +010095 osmo_hexdump(data->data, data->data_len));
96
97 /* parse the APDU data in the USB message */
98 rc = osmo_apdu_segment_in(&ac, data->data, data->data_len,
99 data->flags & CEMU_DATA_F_TPDU_HDR);
100
101 if (rc & APDU_ACT_TX_CAPDU_TO_CARD) {
102 /* there is no pending data coming from the modem */
103 uint8_t apdu_command[sizeof(ac.hdr) + ac.lc.tot];
104 memcpy(apdu_command, &ac.hdr, sizeof(ac.hdr));
105 if (ac.lc.tot)
106 memcpy(apdu_command + sizeof(ac.hdr), ac.dc, ac.lc.tot);
107 /* send APDU to card */
108 ftpdu.buf = apdu_command;
109 ftpdu.len = sizeof(ac.hdr) + ac.lc.tot;
Harald Welte1eec53d2020-05-25 22:48:27 +0200110 osmo_fsm_inst_dispatch(bc->main_fi, MF_E_MDM_TPDU, &ftpdu);
Harald Welte0e968cc2020-02-22 18:16:16 +0100111 } else if (ac.lc.tot > ac.lc.cur) {
112 /* there is pending data from the modem: send procedure byte to get remaining data */
113 osmo_st2_cardem_request_pb_and_rx(ci, ac.hdr.ins, ac.lc.tot - ac.lc.cur);
114 }
115 return 0;
116}
117
118#if 0
119 case SIMTRACE_CMD_DO_ERROR
120 rc = process_do_error(ci, buf, len);
121 break;
122#endif
123
124/*! \brief Process an incoming message from the SIMtrace2 */
125static int process_usb_msg(struct osmo_st2_cardem_inst *ci, uint8_t *buf, int len)
126{
127 struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf;
128 int rc;
129
Harald Welteb97e2fb2020-03-03 21:01:11 +0100130 LOGCI(ci, LOGL_DEBUG, "SIMtrace -> %s\n", osmo_hexdump(buf, len));
Harald Welte0e968cc2020-02-22 18:16:16 +0100131
132 buf += sizeof(*sh);
133
134 switch (sh->msg_type) {
135 case SIMTRACE_MSGT_BD_CEMU_STATUS:
136 rc = process_do_status(ci, buf, len);
137 break;
138 case SIMTRACE_MSGT_DO_CEMU_PTS:
139 rc = process_do_pts(ci, buf, len);
140 break;
141 case SIMTRACE_MSGT_DO_CEMU_RX_DATA:
142 rc = process_do_rx_da(ci, buf, len);
143 break;
144 case SIMTRACE_MSGT_BD_CEMU_CONFIG:
145 /* firmware confirms configuration change; ignore */
146 break;
147 default:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100148 LOGCI(ci, LOGL_ERROR, "unknown simtrace msg type 0x%02x\n", sh->msg_type);
Harald Welte0e968cc2020-02-22 18:16:16 +0100149 rc = -1;
150 break;
151 }
152
153 return rc;
154}
155
156
157/*! \brief Process a STATUS message on IRQ endpoint from the SIMtrace2 */
158static int process_irq_status(struct osmo_st2_cardem_inst *ci, const uint8_t *buf, int len)
159{
160 const struct cardemu_usb_msg_status *status = (struct cardemu_usb_msg_status *) buf;
161 struct bankd_client *bc = ci->priv;
162 struct frontend_phys_status pstatus = {
163 .flags = {
164 .reset_active = status->flags & CEMU_STATUS_F_RESET_ACTIVE,
165 .vcc_present = status->flags & CEMU_STATUS_F_VCC_PRESENT,
166 .clk_active = status->flags & CEMU_STATUS_F_CLK_ACTIVE,
167 .card_present = -1 /* FIXME: make this dependent on board */,
168 },
169 .voltage_mv = status->voltage_mv,
170 .fi = status->fi,
171 .di = status->di,
172 .wi = status->wi,
173 .waiting_time = status->waiting_time,
174 };
175
Harald Welteb97e2fb2020-03-03 21:01:11 +0100176 LOGCI(ci, LOGL_INFO, "SIMtrace IRQ STATUS: flags=0x%x, fi=%u, di=%u, wi=%u wtime=%u\n",
Harald Welte0e968cc2020-02-22 18:16:16 +0100177 status->flags, status->fi, status->di, status->wi,
178 status->waiting_time);
179
180 osmo_fsm_inst_dispatch(bc->main_fi, MF_E_MDM_STATUS_IND, &pstatus);
181 return 0;
182}
183
184static int process_usb_msg_irq(struct osmo_st2_cardem_inst *ci, const uint8_t *buf, unsigned int len)
185{
186 struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf;
187 int rc;
188
Harald Welteb97e2fb2020-03-03 21:01:11 +0100189 LOGCI(ci, LOGL_INFO, "SIMtrace IRQ %s\n", osmo_hexdump(buf, len));
Harald Welte0e968cc2020-02-22 18:16:16 +0100190
191 buf += sizeof(*sh);
192
193 switch (sh->msg_type) {
194 case SIMTRACE_MSGT_BD_CEMU_STATUS:
195 rc = process_irq_status(ci, buf, len);
196 break;
197 default:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100198 LOGCI(ci, LOGL_ERROR, "unknown simtrace msg type 0x%02x\n", sh->msg_type);
Harald Welte0e968cc2020-02-22 18:16:16 +0100199 rc = -1;
200 break;
201 }
202
203 return rc;
204}
205
206static void usb_in_xfer_cb(struct libusb_transfer *xfer)
207{
208 struct osmo_st2_cardem_inst *ci = xfer->user_data;
209 int rc;
210
211 switch (xfer->status) {
212 case LIBUSB_TRANSFER_COMPLETED:
213 /* hand the message up the stack */
214 process_usb_msg(ci, xfer->buffer, xfer->actual_length);
215 break;
216 case LIBUSB_TRANSFER_NO_DEVICE:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100217 LOGCI(ci, LOGL_FATAL, "USB device disappeared\n");
Harald Welte0e968cc2020-02-22 18:16:16 +0100218 exit(1);
219 break;
220 default:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100221 LOGCI(ci, LOGL_FATAL, "USB IN transfer failed, status=%u\n", xfer->status);
Harald Welte0e968cc2020-02-22 18:16:16 +0100222 exit(1);
223 break;
224 }
225
226 /* re-submit the IN transfer */
227 rc = libusb_submit_transfer(xfer);
228 OSMO_ASSERT(rc == 0);
229}
230
231
232static void allocate_and_submit_in(struct osmo_st2_cardem_inst *ci)
233{
234 struct osmo_st2_transport *transp = ci->slot->transp;
235 struct libusb_transfer *xfer;
236 int rc;
237
238 xfer = libusb_alloc_transfer(0);
239 OSMO_ASSERT(xfer);
240 xfer->dev_handle = transp->usb_devh;
241 xfer->flags = 0;
242 xfer->type = LIBUSB_TRANSFER_TYPE_BULK;
243 xfer->endpoint = transp->usb_ep.in;
244 xfer->timeout = 0;
245 xfer->user_data = ci;
246 xfer->length = 16*256;
247
248 xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length);
249 OSMO_ASSERT(xfer->buffer);
250 xfer->callback = usb_in_xfer_cb;
251
252 /* submit the IN transfer */
253 rc = libusb_submit_transfer(xfer);
254 OSMO_ASSERT(rc == 0);
255}
256
257
258static void usb_irq_xfer_cb(struct libusb_transfer *xfer)
259{
260 struct osmo_st2_cardem_inst *ci = xfer->user_data;
261 int rc;
262
263 switch (xfer->status) {
264 case LIBUSB_TRANSFER_COMPLETED:
265 process_usb_msg_irq(ci, xfer->buffer, xfer->actual_length);
266 break;
267 case LIBUSB_TRANSFER_NO_DEVICE:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100268 LOGCI(ci, LOGL_FATAL, "USB device disappeared\n");
Harald Welte0e968cc2020-02-22 18:16:16 +0100269 exit(1);
270 break;
271 default:
Harald Welteb97e2fb2020-03-03 21:01:11 +0100272 LOGCI(ci, LOGL_FATAL, "USB IN transfer failed, status=%u\n", xfer->status);
Harald Welte0e968cc2020-02-22 18:16:16 +0100273 exit(1);
274 break;
275 }
276
277 /* re-submit the IN transfer */
278 rc = libusb_submit_transfer(xfer);
279 OSMO_ASSERT(rc == 0);
280}
281
282
283static void allocate_and_submit_irq(struct osmo_st2_cardem_inst *ci)
284{
285 struct osmo_st2_transport *transp = ci->slot->transp;
286 struct libusb_transfer *xfer;
287 int rc;
288
289 xfer = libusb_alloc_transfer(0);
290 OSMO_ASSERT(xfer);
291 xfer->dev_handle = transp->usb_devh;
292 xfer->flags = 0;
293 xfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT;
294 xfer->endpoint = transp->usb_ep.irq_in;
295 xfer->timeout = 0;
296 xfer->user_data = ci;
297 xfer->length = 64;
298
299 xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length);
300 OSMO_ASSERT(xfer->buffer);
301 xfer->callback = usb_irq_xfer_cb;
302
303 /* submit the IN transfer */
304 rc = libusb_submit_transfer(xfer);
305 OSMO_ASSERT(rc == 0);
306}
307
308
309
310
311/***********************************************************************
312 * simtrace2 frontend code to remsim-client
313 ***********************************************************************/
314
315int frontend_request_card_insert(struct bankd_client *bc)
316{
317 struct osmo_st2_cardem_inst *ci = bc->cardem;
318 return osmo_st2_cardem_request_card_insert(ci, true);
319}
320
Harald Welte7b87ba12021-09-08 21:38:35 +0200321int frontend_request_card_remove(struct bankd_client *bc)
322{
323 struct osmo_st2_cardem_inst *ci = bc->cardem;
324 return osmo_st2_cardem_request_card_insert(ci, false);
325}
326
Harald Welte0e968cc2020-02-22 18:16:16 +0100327int frontend_request_sim_remote(struct bankd_client *bc)
328{
329 struct osmo_st2_cardem_inst *ci = bc->cardem;
330 return osmo_st2_modem_sim_select_remote(ci->slot);
331}
332
Harald Welte7b87ba12021-09-08 21:38:35 +0200333int frontend_request_sim_local(struct bankd_client *bc)
334{
335 struct osmo_st2_cardem_inst *ci = bc->cardem;
336 return osmo_st2_modem_sim_select_local(ci->slot);
337}
338
Harald Welte0e968cc2020-02-22 18:16:16 +0100339int frontend_request_modem_reset(struct bankd_client *bc)
340{
341 struct osmo_st2_cardem_inst *ci = bc->cardem;
342 return osmo_st2_modem_reset_pulse(ci->slot, 300);
343}
344
345int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len)
346{
347 struct osmo_st2_cardem_inst *ci = bc->cardem;
348 // save SW to our current APDU context
349 ac.sw[0] = data[len-2];
Harald Welte57b21d42020-05-28 16:28:47 +0200350 ac.sw[1] = data[len-1];
Harald Welte0e968cc2020-02-22 18:16:16 +0100351
Harald Welteb97e2fb2020-03-03 21:01:11 +0100352 LOGCI(ci, LOGL_DEBUG, "SIMtrace <= SW=0x%02x%02x, len_rx=%zu\n", ac.sw[0], ac.sw[1], len-2);
Harald Welte0e968cc2020-02-22 18:16:16 +0100353 if (len > 2) { // send PB and data to modem
354 osmo_st2_cardem_request_pb_and_tx(ci, ac.hdr.ins, data, len-2);
355 }
356 osmo_st2_cardem_request_sw_tx(ci, ac.sw); // send SW to modem
357 return 0;
358}
359
360int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len)
361{
362 struct osmo_st2_cardem_inst *ci = bc->cardem;
363 return osmo_st2_cardem_request_set_atr(ci, data, len);
364}
365
366int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts)
367{
368 /* we currently don't propagate bankd status to cardem */
369 return 0;
370}
371
Harald Weltee580c932020-05-24 16:03:56 +0200372int frontend_append_script_env(struct bankd_client *bc, char **env, int i, size_t max_env)
Harald Welte0e968cc2020-02-22 18:16:16 +0100373{
374 struct osmo_st2_cardem_inst *ci = bc->cardem;
Harald Welte0e968cc2020-02-22 18:16:16 +0100375
376 if (max_env < 4)
377 return -ENOSPC;
378
379 env[i++] = talloc_asprintf(env, "REMSIM_USB_PATH=%s", ci->usb_path);
380 /* TODO: Configuration; Altsetting */
381 env[i++] = talloc_asprintf(env, "REMSIM_USB_INTERFACE=%u", bc->cfg->usb.if_num);
382
383 return i;
384}
385
386/* FIXME: This must be cleaned up */
387static struct osmo_st2_transport _transp;
388static struct osmo_st2_slot _slot = {
389 .transp = &_transp,
390 .slot_nr = 0,
391};
392
393int client_user_main(struct bankd_client *bc)
394{
395 struct usb_interface_match _ifm, *ifm = &_ifm;
396 struct osmo_st2_transport *transp;
397 struct osmo_st2_cardem_inst *ci;
398 struct client_config *cfg = bc->cfg;
399 int rc, i;
400
401 rc = osmo_libusb_init(NULL);
402 if (rc < 0) {
Harald Welte7293e7b2021-12-08 21:29:11 +0100403 LOGP(DMAIN, LOGL_ERROR, "libusb initialization failed\n");
Harald Welte0e968cc2020-02-22 18:16:16 +0100404 return rc;
405 }
406
407 ci = talloc_zero(bc, struct osmo_st2_cardem_inst);
408 OSMO_ASSERT(ci);
409 ci->slot = &_slot;
410 transp = ci->slot->transp;
411 ci->priv = bc;
412 bc->cardem = ci;
413
414 ifm->vendor = cfg->usb.vendor_id;
415 ifm->product = cfg->usb.product_id;
416 ifm->configuration = cfg->usb.config_id;
417 ifm->interface = cfg->usb.if_num;
418 ifm->altsetting = cfg->usb.altsetting;
419 ifm->addr = cfg->usb.addr;
420 if (cfg->usb.path)
421 osmo_strlcpy(ifm->path, cfg->usb.path, sizeof(ifm->path));
Harald Weltee0438262020-05-25 18:15:15 +0200422 transp->udp_fd = -1;
Harald Weltedd337252020-05-25 22:38:24 +0200423 transp->usb_async = true;
Harald Welte0e968cc2020-02-22 18:16:16 +0100424 transp->usb_devh = osmo_libusb_open_claim_interface(NULL, NULL, ifm);
425 if (!transp->usb_devh) {
Harald Welte7293e7b2021-12-08 21:29:11 +0100426 LOGP(DMAIN, LOGL_ERROR, "can't open USB device\n");
Harald Welte0e968cc2020-02-22 18:16:16 +0100427 return -1;
428 }
429
430 /* (re)determine the USB path of the opened device */
431 talloc_free(ci->usb_path);
432 ci->usb_path = osmo_libusb_dev_get_path_c(ci, libusb_get_device(transp->usb_devh));
433
434 rc = libusb_claim_interface(transp->usb_devh, cfg->usb.if_num);
435 if (rc < 0) {
Harald Welte7293e7b2021-12-08 21:29:11 +0100436 LOGP(DMAIN, LOGL_ERROR, "can't claim interface %d; rc=%d\n", cfg->usb.if_num, rc);
Harald Welte0e968cc2020-02-22 18:16:16 +0100437 goto close_exit;
438 }
439
440 rc = osmo_libusb_get_ep_addrs(transp->usb_devh, cfg->usb.if_num, &transp->usb_ep.out,
441 &transp->usb_ep.in, &transp->usb_ep.irq_in);
442 if (rc < 0) {
Harald Welte7293e7b2021-12-08 21:29:11 +0100443 LOGP(DMAIN, LOGL_ERROR, "can't obtain EP addrs; rc=%d\n", rc);
Harald Welte0e968cc2020-02-22 18:16:16 +0100444 goto close_exit;
445 }
446
447 allocate_and_submit_irq(ci);
448 /* submit multiple IN URB in order to work around OS#4409 */
449 for (i = 0; i < 4; i++)
450 allocate_and_submit_in(ci);
451
452 /* request firmware to generate STATUS on IRQ endpoint */
453 osmo_st2_cardem_request_config(ci, CEMU_FEAT_F_STATUS_IRQ);
454
455 while (1) {
456 osmo_select_main(0);
457 }
458
459 return 0;
460
461close_exit:
462 if (transp->usb_devh)
463 libusb_close(transp->usb_devh);
464 osmo_libusb_exit(NULL);
465
466 return -1;
467}