From 8e7fca32554e6beaea234bf5920795e6fea26848 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 7 May 2017 16:14:33 +0200 Subject: [PATCH] migrate from req_ctx to msgb We now generalize the USB communiction and abandon the 'req_ctx' structure inherited from openpcd. Instead we use the libosmocore 'msgb' structure to handle incoming and outgoing USB tranfers. We also use linuxlist-based msgb-queues for each endpoint. --- firmware/Makefile | 2 +- firmware/apps/cardem/Makefile | 2 +- firmware/apps/cardem/main.c | 3 - firmware/apps/dfu/main.c | 2 - firmware/apps/triple_play/Makefile | 2 +- firmware/libboard/qmod/source/board_qmod.c | 4 +- firmware/libcommon/include/card_emu.h | 4 +- firmware/libcommon/include/simtrace.h | 4 - firmware/libcommon/include/talloc.h | 25 +++ firmware/libcommon/include/usb_buf.h | 28 +++ firmware/libcommon/source/card_emu.c | 168 +++++++++--------- .../libcommon/source/host_communication.c | 148 +++++++++------ firmware/libcommon/source/mode_cardemu.c | 93 +++++----- firmware/libcommon/source/pseudo_talloc.c | 69 +++++++ firmware/libcommon/source/req_ctx.c | 140 --------------- firmware/libcommon/source/usb_buf.c | 76 ++++++++ firmware/test/Makefile | 3 +- firmware/test/card_emu_tests.c | 103 ++++++----- 18 files changed, 494 insertions(+), 382 deletions(-) create mode 100644 firmware/libcommon/include/talloc.h create mode 100644 firmware/libcommon/include/usb_buf.h create mode 100644 firmware/libcommon/source/pseudo_talloc.c delete mode 100644 firmware/libcommon/source/req_ctx.c create mode 100644 firmware/libcommon/source/usb_buf.c diff --git a/firmware/Makefile b/firmware/Makefile index a9331027..50cc2dd7 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -97,7 +97,7 @@ C_LIBCHIP = $(notdir $(wildcard $(AT91LIB)/libchip_sam3s/source/*.c) $(wildcard C_LIBUSB = USBDescriptors.c USBRequests.c USBD.c USBDCallbacks.c USBDDriver.c USBDDriverCallbacks.c C_LIBUSB_RT = dfu.c dfu_runtime.c C_LIBUSB_DFU = dfu.c dfu_desc.c dfu_driver.c -C_LIBCOMMON = string.c stdio.c fputs.c req_ctx.c ringbuffer.c +C_LIBCOMMON = string.c stdio.c fputs.c usb_buf.c ringbuffer.c pseudo_talloc.c host_communication.c C_BOARD = $(notdir $(wildcard libboard/common/source/*.c)) C_BOARD += $(notdir $(wildcard libboard/$(BOARD)/source/*.c)) diff --git a/firmware/apps/cardem/Makefile b/firmware/apps/cardem/Makefile index 8a2f29b3..8129b875 100644 --- a/firmware/apps/cardem/Makefile +++ b/firmware/apps/cardem/Makefile @@ -1,3 +1,3 @@ C_FILES += $(C_LIBUSB_RT) -C_FILES += card_emu.c ccid.c host_communication.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c +C_FILES += card_emu.c ccid.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c diff --git a/firmware/apps/cardem/main.c b/firmware/apps/cardem/main.c index 7d90eb0d..9adda569 100644 --- a/firmware/apps/cardem/main.c +++ b/firmware/apps/cardem/main.c @@ -6,7 +6,6 @@ #include "board.h" #include "simtrace.h" #include "utils.h" -#include "req_ctx.h" #include "osmocom/core/timer.h" unsigned int g_unique_id[4]; @@ -128,8 +127,6 @@ extern int main(void) WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT | (WDT_GetPeriod(500) << 16) | WDT_GetPeriod(500)); - req_ctx_init(); - PIO_InitializeInterrupts(0); EEFC_ReadUniqueID(g_unique_id); diff --git a/firmware/apps/dfu/main.c b/firmware/apps/dfu/main.c index 2b44f01d..5f683ce0 100644 --- a/firmware/apps/dfu/main.c +++ b/firmware/apps/dfu/main.c @@ -190,8 +190,6 @@ extern int main(void) WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT | (WDT_GetPeriod(500) << 16) | WDT_GetPeriod(500)); - //req_ctx_init(); - PIO_InitializeInterrupts(0); EEFC_ReadUniqueID(g_unique_id); diff --git a/firmware/apps/triple_play/Makefile b/firmware/apps/triple_play/Makefile index 8a2f29b3..8129b875 100644 --- a/firmware/apps/triple_play/Makefile +++ b/firmware/apps/triple_play/Makefile @@ -1,3 +1,3 @@ C_FILES += $(C_LIBUSB_RT) -C_FILES += card_emu.c ccid.c host_communication.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c +C_FILES += card_emu.c ccid.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c diff --git a/firmware/libboard/qmod/source/board_qmod.c b/firmware/libboard/qmod/source/board_qmod.c index 9f25a4b3..24d5649b 100644 --- a/firmware/libboard/qmod/source/board_qmod.c +++ b/firmware/libboard/qmod/source/board_qmod.c @@ -4,12 +4,12 @@ #include "board.h" #include "simtrace.h" #include "utils.h" -#include "req_ctx.h" #include "wwan_led.h" #include "wwan_perst.h" #include "boardver_adc.h" #include "card_pres.h" #include "osmocom/core/timer.h" +#include "usb_buf.h" static const Pin pin_hubpwr_override = PIN_PRTPWR_OVERRIDE; static const Pin pin_hub_rst = {PIO_PA13, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}; @@ -231,6 +231,8 @@ void board_exec_dbg_cmd(int ch) void board_main_top(void) { + usb_buf_init(); + wwan_led_init(); wwan_perst_init(); diff --git a/firmware/libcommon/include/card_emu.h b/firmware/libcommon/include/card_emu.h index df23ce47..43279f8a 100644 --- a/firmware/libcommon/include/card_emu.h +++ b/firmware/libcommon/include/card_emu.h @@ -10,7 +10,8 @@ enum card_io { CARD_IO_CLK, }; -struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan); +struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, + uint8_t in_ep, uint8_t irq_ep); /* process a single byte received from the reader */ void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte); @@ -25,7 +26,6 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active); int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len); struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch); -struct llist_head *card_emu_get_usb_tx_queue(struct card_handle *ch); void card_emu_have_new_uart_tx(struct card_handle *ch); void card_emu_report_status(struct card_handle *ch); diff --git a/firmware/libcommon/include/simtrace.h b/firmware/libcommon/include/simtrace.h index 2a54d76f..b9589f33 100644 --- a/firmware/libcommon/include/simtrace.h +++ b/firmware/libcommon/include/simtrace.h @@ -113,8 +113,4 @@ extern void mode_cardemu_usart1_irq(void); void Timer_Init( void ); void TC0_Counter_Reset( void ); -struct llist_head; -int usb_refill_to_host(struct llist_head *queue, uint32_t ep); -int usb_refill_from_host(struct llist_head *queue, int ep); - #endif /* SIMTRACE_H */ diff --git a/firmware/libcommon/include/talloc.h b/firmware/libcommon/include/talloc.h new file mode 100644 index 00000000..f2d9666e --- /dev/null +++ b/firmware/libcommon/include/talloc.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +/* minimalistic emulation of core talloc API functions used by msgb.c */ + +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) +void *_talloc_zero(const void *ctx, size_t size, const char *name); + +#define talloc_free(ctx) _talloc_free(ctx, __location__) +int _talloc_free(void *ptr, const char *location); + +/* Unsupported! */ +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +void *talloc_named_const(const void *context, size_t size, const char *name); +void talloc_set_name_const(const void *ptr, const char *name); +char *talloc_strdup(const void *t, const char *p); +void *talloc_pool(const void *context, size_t size); diff --git a/firmware/libcommon/include/usb_buf.h b/firmware/libcommon/include/usb_buf.h new file mode 100644 index 00000000..dd16531e --- /dev/null +++ b/firmware/libcommon/include/usb_buf.h @@ -0,0 +1,28 @@ +#pragma once + +#include "osmocom/core/linuxlist.h" +#include "osmocom/core/msgb.h" + +/* buffered USB endpoint (with queue of msgb) */ +struct usb_buffered_ep { + /* endpoint number */ + uint8_t ep; + /* OUT endpoint (1) or IN/IRQ (0)? */ + uint8_t out_from_host; + /* currently any transfer in progress? */ + volatile uint32_t in_progress; + /* Tx queue (IN) / Rx queue (OUT) */ + struct llist_head queue; +}; + +struct msgb *usb_buf_alloc(uint8_t ep); +void usb_buf_free(struct msgb *msg); +int usb_buf_submit(struct msgb *msg); +struct llist_head *usb_get_queue(uint8_t ep); +int usb_drain_queue(uint8_t ep); + +void usb_buf_init(void); +struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep); + +int usb_refill_to_host(uint8_t ep); +int usb_refill_from_host(uint8_t ep); diff --git a/firmware/libcommon/source/card_emu.c b/firmware/libcommon/source/card_emu.c index a1be34cf..6edf05e1 100644 --- a/firmware/libcommon/source/card_emu.c +++ b/firmware/libcommon/source/card_emu.c @@ -1,5 +1,5 @@ /* ISO7816-3 state machine for the card side */ -/* (C) 2010-2015 by Harald Welte +/* (C) 2010-2017 by Harald Welte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,9 +31,10 @@ #include "iso7816_fidi.h" #include "tc_etu.h" #include "card_emu.h" -#include "req_ctx.h" #include "cardemu_prot.h" +#include "usb_buf.h" #include "osmocom/core/linuxlist.h" +#include "osmocom/core/msgb.h" #define NUM_SLOTS 2 @@ -114,6 +115,9 @@ struct card_handle { uint8_t tc_chan; /* TC channel number */ uint8_t uart_chan; /* UART channel */ + uint8_t in_ep; /* USB IN EP */ + uint8_t irq_ep; /* USB IN EP */ + uint32_t waiting_time; /* in clocks */ /* ATR state machine */ @@ -138,10 +142,9 @@ struct card_handle { uint8_t hdr[5]; /* CLA INS P1 P2 P3 */ } tpdu; - struct req_ctx *uart_rx_ctx; /* UART RX -> USB TX */ - struct req_ctx *uart_tx_ctx; /* USB RX -> UART TX */ + struct msgb *uart_rx_msg; /* UART RX -> USB TX */ + struct msgb *uart_tx_msg; /* USB RX -> UART TX */ - struct llist_head usb_tx_queue; struct llist_head uart_tx_queue; struct { @@ -151,11 +154,6 @@ struct card_handle { } stats; }; -struct llist_head *card_emu_get_usb_tx_queue(struct card_handle *ch) -{ - return &ch->usb_tx_queue; -} - struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch) { return &ch->uart_tx_queue; @@ -166,24 +164,23 @@ static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss); static void flush_rx_buffer(struct card_handle *ch) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_rx_data *rd; + uint32_t data_len; - rctx = ch->uart_rx_ctx; - if (!rctx) + msg = ch->uart_rx_msg; + if (!msg) return; - ch->uart_rx_ctx = NULL; + ch->uart_rx_msg = NULL; /* store length of data payload fild in header */ - rd = (struct cardemu_usb_msg_rx_data *) rctx->data; - rd->data_len = rctx->idx; - rd->hdr.msg_len = sizeof(*rd) + rd->data_len; + rd = (struct cardemu_usb_msg_rx_data *) msg->l1h; + msg->l2h = &rd->data; + rd->data_len = msgb_l2len(msg); + rd->hdr.msg_len = msgb_length(msg); - req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); - /* no need for irqsafe operation, as the usb_tx_queue is - * processed only by the main loop context */ - llist_add_tail(&rctx->list, &ch->usb_tx_queue); + usb_buf_submit(msg); } /* convert a non-contiguous PTS request/responsei into a contiguous @@ -223,23 +220,22 @@ static uint8_t csum_pts(const uint8_t *in) static void flush_pts(struct card_handle *ch) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_pts_info *ptsi; - rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); - if (!rctx) + msg = usb_buf_alloc(ch->in_ep); + if (!msg) return; - ptsi = (struct cardemu_usb_msg_pts_info *) rctx->data; + msg->l1h = msgb_put(msg, sizeof(*ptsi)); + msg->l2h = msg->l1h + sizeof(ptsi->hdr); + ptsi = (struct cardemu_usb_msg_pts_info *) msg->l1h; ptsi->hdr.msg_type = CEMU_USB_MSGT_DO_PTS; ptsi->hdr.msg_len = sizeof(*ptsi); ptsi->pts_len = serialize_pts(ptsi->req, ch->pts.req); serialize_pts(ptsi->resp, ch->pts.resp); - req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); - /* no need for irqsafe operation, as the usb_tx_queue is - * processed only by the main loop context */ - llist_add_tail(&rctx->list, &ch->usb_tx_queue); + usb_buf_submit(msg); } static void emu_update_fidi(struct card_handle *ch) @@ -502,37 +498,35 @@ static unsigned int t0_num_data_bytes(uint8_t p3, int reader_to_card) /* add a just-received TPDU byte (from reader) to USB buffer */ static void add_tpdu_byte(struct card_handle *ch, uint8_t byte) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_rx_data *rd; unsigned int num_data_bytes = t0_num_data_bytes(ch->tpdu.hdr[_P3], 0); /* ensure we have a buffer */ - if (!ch->uart_rx_ctx) { - rctx = ch->uart_rx_ctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); - if (!ch->uart_rx_ctx) { + if (!ch->uart_rx_msg) { + msg = ch->uart_rx_msg = usb_buf_alloc(ch->in_ep); + if (!ch->uart_rx_msg) { TRACE_ERROR("%u: Received UART byte but ENOMEM\r\n", ch->num); return; } - rd = (struct cardemu_usb_msg_rx_data *) ch->uart_rx_ctx->data; + msg->l1h = msgb_put(msg, sizeof(*rd)); + rd = (struct cardemu_usb_msg_rx_data *) msg->l1h; + msg->l2h = msg->l1h + sizeof(rd->hdr); cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA); - rctx->tot_len = sizeof(*rd); - rctx->idx = 0; } else - rctx = ch->uart_rx_ctx; + msg = ch->uart_rx_msg; - rd = (struct cardemu_usb_msg_rx_data *) rctx->data; - - rd->data[rctx->idx++] = byte; - rctx->tot_len++; + rd = (struct cardemu_usb_msg_rx_data *) msg->l1h; + msgb_put_u8(msg, byte); /* check if the buffer is full. If so, send it */ - if (rctx->tot_len >= sizeof(*rd) + num_data_bytes) { + if (msgb_length(msg) >= sizeof(*rd) + num_data_bytes) { rd->flags |= CEMU_DATA_F_FINAL; flush_rx_buffer(ch); /* We need to transmit the SW now, */ set_tpdu_state(ch, TPDU_S_WAIT_TX); - } else if (rctx->tot_len >= rctx->size) + } else if (msgb_tailroom(msg) <= 0) flush_rx_buffer(ch); } @@ -590,8 +584,9 @@ static enum tpdu_state next_tpdu_state(struct card_handle *ch) static void send_tpdu_header(struct card_handle *ch) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_rx_data *rd; + uint8_t *cur; TRACE_INFO("%u: %s: %02x %02x %02x %02x %02x\r\n", ch->num, __func__, @@ -600,32 +595,32 @@ static void send_tpdu_header(struct card_handle *ch) ch->tpdu.hdr[4]); /* if we already/still have a context, send it off */ - if (ch->uart_rx_ctx) { + if (ch->uart_rx_msg) { TRACE_DEBUG("%u: have old buffer\r\n", ch->num); - if (ch->uart_rx_ctx->idx) { + if (msgb_l2len(ch->uart_rx_msg)) { TRACE_DEBUG("%u: flushing old buffer\r\n", ch->num); flush_rx_buffer(ch); } - } else { - TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num); - /* ensure we have a new buffer */ - ch->uart_rx_ctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); - if (!ch->uart_rx_ctx) { - TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__); - return; - } } - rctx = ch->uart_rx_ctx; - rd = (struct cardemu_usb_msg_rx_data *) rctx->data; + TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num); + /* ensure we have a new buffer */ + ch->uart_rx_msg = usb_buf_alloc(ch->in_ep); + if (!ch->uart_rx_msg) { + TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__); + return; + } + msg = ch->uart_rx_msg; + msg->l1h = msgb_put(msg, sizeof(*rd)); + rd = (struct cardemu_usb_msg_rx_data *) msg->l1h; - /* initializ header */ + /* initialize header */ + msg->l2h = msg->l1h + sizeof(rd->hdr); cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA); rd->flags = CEMU_DATA_F_TPDU_HDR; - rctx->tot_len = sizeof(*rd) + sizeof(ch->tpdu.hdr); - rctx->idx = sizeof(ch->tpdu.hdr); /* copy TPDU header to data field */ - memcpy(rd->data, ch->tpdu.hdr, sizeof(ch->tpdu.hdr)); + cur = msgb_put(msg, sizeof(ch->tpdu.hdr)); + memcpy(cur, ch->tpdu.hdr, sizeof(ch->tpdu.hdr)); /* rd->data_len is set in flush_rx_buffer() */ flush_rx_buffer(ch); @@ -674,33 +669,29 @@ process_byte_tpdu(struct card_handle *ch, uint8_t byte) /* tx a single byte to be transmitted to the reader */ static int tx_byte_tpdu(struct card_handle *ch) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_tx_data *td; uint8_t byte; /* ensure we are aware of any data that might be pending for * transmit */ - if (!ch->uart_tx_ctx) { + if (!ch->uart_tx_msg) { /* uart_tx_queue is filled from main loop, so no need * for irq-safe operations */ if (llist_empty(&ch->uart_tx_queue)) return 0; /* dequeue first at head */ - ch->uart_tx_ctx = llist_entry(ch->uart_tx_queue.next, - struct req_ctx, list); - llist_del(&ch->uart_tx_ctx->list); - req_ctx_set_state(ch->uart_tx_ctx, RCTX_S_UART_TX_BUSY); - - /* start with index zero */ - ch->uart_tx_ctx->idx = 0; - + ch->uart_tx_msg = msgb_dequeue(&ch->uart_tx_queue); + ch->uart_tx_msg->l1h = ch->uart_tx_msg->data; + msg = ch->uart_tx_msg; + msgb_pull(msg, sizeof(*td)); } - rctx = ch->uart_tx_ctx; - td = (struct cardemu_usb_msg_tx_data *) rctx->data; + msg = ch->uart_tx_msg; + td = (struct cardemu_usb_msg_tx_data *) msg->l1h; - /* take the next pending byte out of the rctx */ - byte = td->data[rctx->idx++]; + /* take the next pending byte out of the msgb */ + byte = msgb_pull_u8(msg); card_emu_uart_tx(ch->uart_chan, byte); @@ -719,8 +710,7 @@ static int tx_byte_tpdu(struct card_handle *ch) } /* check if the buffer has now been fully transmitted */ - if ((rctx->idx >= td->data_len) || - (td->data + rctx->idx >= rctx->data + rctx->tot_len)) { + if (msgb_length(msg) == 0) { if (td->flags & CEMU_DATA_F_PB_AND_RX) { /* we have just sent the procedure byte and now * need to continue receiving */ @@ -733,8 +723,8 @@ static int tx_byte_tpdu(struct card_handle *ch) card_set_state(ch, ISO_S_WAIT_TPDU); } } - req_ctx_set_state(rctx, RCTX_S_FREE); - ch->uart_tx_ctx = NULL; + usb_buf_free(msg); + ch->uart_tx_msg = NULL; } return 1; @@ -836,15 +826,16 @@ void card_emu_have_new_uart_tx(struct card_handle *ch) void card_emu_report_status(struct card_handle *ch) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_status *sts; - rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); - if (!rctx) + msg = usb_buf_alloc(ch->in_ep); + if (!msg) return; - - rctx->tot_len = sizeof(*sts); - sts = (struct cardemu_usb_msg_status *)rctx->data; + + msg->l1h = msgb_put(msg, sizeof(*sts)); + msg->l2h = msg->l1h + sizeof(sts->hdr); + sts = (struct cardemu_usb_msg_status *) msg->l1h; sts->hdr.msg_type = CEMU_USB_MSGT_DO_STATUS; sts->hdr.msg_len = sizeof(*sts); sts->flags = 0; @@ -860,8 +851,7 @@ void card_emu_report_status(struct card_handle *ch) sts->wi = ch->wi; sts->waiting_time = ch->waiting_time; - llist_add_tail(&rctx->list, &ch->usb_tx_queue); - req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); + usb_buf_submit(msg); } /* hardware driver informs us that a card I/O signal has changed */ @@ -957,7 +947,8 @@ static const uint8_t default_atr[] = { 0x3B, 0x02, 0x14, 0x50 }; static struct card_handle card_handles[NUM_SLOTS]; -struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan) +struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan, + uint8_t in_ep, uint8_t irq_ep) { struct card_handle *ch; @@ -968,11 +959,12 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar memset(ch, 0, sizeof(*ch)); - INIT_LLIST_HEAD(&ch->usb_tx_queue); INIT_LLIST_HEAD(&ch->uart_tx_queue); /* initialize the card_handle with reasonabe defaults */ ch->num = slot_num; + ch->irq_ep = irq_ep; + ch->in_ep = in_ep; ch->state = ISO_S_WAIT_POWER; ch->vcc_active = 0; ch->in_reset = 1; diff --git a/firmware/libcommon/source/host_communication.c b/firmware/libcommon/source/host_communication.c index 816c7a18..acf931ea 100644 --- a/firmware/libcommon/source/host_communication.c +++ b/firmware/libcommon/source/host_communication.c @@ -1,126 +1,168 @@ -//#define TRACE_LEVEL 6 +#include "board.h" +#include "llist_irqsafe.h" +#include "usb_buf.h" +#include "osmocom/core/linuxlist.h" +#include "osmocom/core/msgb.h" #include -#include "board.h" -#include "req_ctx.h" -#include "osmocom/core/linuxlist.h" -#include "llist_irqsafe.h" - -static volatile uint32_t usbep_in_progress[BOARD_USB_NUMENDPOINTS]; +/*********************************************************************** + * USBD Integration API + ***********************************************************************/ /* call-back after (successful?) transfer of a buffer */ static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred, uint32_t remaining) { - struct req_ctx *rctx = (struct req_ctx *) arg; + struct msgb *msg = (struct msgb *) arg; + struct usb_buffered_ep *bep = msg->dst; - TRACE_DEBUG("%s (EP=%u)\r\n", __func__, rctx->ep); + TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep); __disable_irq(); - usbep_in_progress[rctx->ep]--; + bep->in_progress--; __enable_irq(); - TRACE_DEBUG("%u: in_progress=%d\n", rctx->ep, usbep_in_progress[rctx->ep]); + TRACE_DEBUG("%u: in_progress=%d\n", bep->ep, bep->in_progress); if (status != USBD_STATUS_SUCCESS) TRACE_ERROR("%s error, status=%d\n", __func__, status); - /* release request contxt to pool */ - req_ctx_set_state(rctx, RCTX_S_FREE); + usb_buf_free(msg); } -int usb_refill_to_host(struct llist_head *queue, uint32_t ep) +int usb_refill_to_host(uint8_t ep) { - struct req_ctx *rctx; + struct usb_buffered_ep *bep = usb_get_buf_ep(ep); + struct msgb *msg; int rc; +#if 0 + if (bep->out_from_host) { + TRACE_ERROR("EP 0x%02x is not IN\r\n", bep->ep); + return -EINVAL; + } +#endif + __disable_irq(); - if (usbep_in_progress[ep]) { + if (bep->in_progress) { __enable_irq(); return 0; } - if (llist_empty(queue)) { + if (llist_empty(&bep->queue)) { __enable_irq(); return 0; } - usbep_in_progress[ep]++; + bep->in_progress++; - rctx = llist_entry(queue->next, struct req_ctx, list); - llist_del(&rctx->list); + msg = msgb_dequeue(&bep->queue); __enable_irq(); - TRACE_DEBUG("%u: in_progress=%d\n", ep, usbep_in_progress[ep]); - TRACE_DEBUG("%s (EP=%u)\r\n", __func__, ep); + TRACE_DEBUG("%s (EP=0x%02x), in_progress=%d\r\n", __func__, ep, bep->in_progress); - req_ctx_set_state(rctx, RCTX_S_USB_TX_BUSY); - rctx->ep = ep; + msg->dst = bep; - rc = USBD_Write(ep, rctx->data, rctx->tot_len, - (TransferCallback) &usb_write_cb, rctx); + rc = USBD_Write(ep, msgb_data(msg), msgb_length(msg), + (TransferCallback) &usb_write_cb, msg); if (rc != USBD_STATUS_SUCCESS) { TRACE_ERROR("%s error %x\n", __func__, rc); - req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); + /* re-insert to head of queue */ + llist_add_irqsafe(&msg->list, &bep->queue); __disable_irq(); - usbep_in_progress[ep]--; + bep->in_progress--; __enable_irq(); - TRACE_DEBUG("%u: in_progress=%d\n", ep, usbep_in_progress[ep]); + TRACE_DEBUG("%02x: in_progress=%d\n", bep->ep, bep->in_progress); return 0; } return 1; } +/* call-back after (successful?) transfer of a buffer */ static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred, uint32_t remaining) { - struct req_ctx *rctx = (struct req_ctx *) arg; - struct llist_head *queue = (struct llist_head *) usbep_in_progress[rctx->ep]; + struct msgb *msg = (struct msgb *) arg; + struct usb_buffered_ep *bep = msg->dst; TRACE_DEBUG("%s (EP=%u, len=%u, q=%p)\r\n", __func__, - rctx->ep, transferred, queue); + bep->ep, transferred, &bep->queue); - usbep_in_progress[rctx->ep] = 0; + bep->in_progress = 0; if (status != USBD_STATUS_SUCCESS) { TRACE_ERROR("%s error, status=%d\n", __func__, status); - /* release request contxt to pool */ - req_ctx_put(rctx); + usb_buf_free(msg); return; } - rctx->tot_len = transferred; - req_ctx_set_state(rctx, RCTX_S_MAIN_PROCESSING); - llist_add_tail_irqsafe(&rctx->list, queue); + msgb_put(msg, transferred); + llist_add_tail_irqsafe(&msg->list, &bep->queue); } -int usb_refill_from_host(struct llist_head *queue, int ep) +int usb_refill_from_host(uint8_t ep) { - struct req_ctx *rctx; + struct usb_buffered_ep *bep = usb_get_buf_ep(ep); + struct msgb *msg; int rc; - if (usbep_in_progress[ep]) +#if 0 + if (!bep->out_from_host) { + TRACE_ERROR("EP 0x%02x is not OUT\r\n", bep->ep); + return -EINVAL; + } +#endif + + if (bep->in_progress) return 0; - TRACE_DEBUG("%s (EP=%u)\r\n", __func__, ep); + TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep); - rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_USB_RX_BUSY); - if (!rctx) + msg = usb_buf_alloc(bep->ep); + if (!msg) return -ENOMEM; + msg->dst = bep; + msg->l1h = msg->head; - rctx->ep = ep; - usbep_in_progress[ep] = (uint32_t) queue; - - rc = USBD_Read(ep, rctx->data, rctx->size, - (TransferCallback) &usb_read_cb, rctx); + bep->in_progress = 1; + rc = USBD_Read(ep, msg->head, msgb_tailroom(msg), + (TransferCallback) &usb_read_cb, msg); if (rc != USBD_STATUS_SUCCESS) { - TRACE_ERROR("%s error %x\n", __func__, rc); - req_ctx_put(rctx); - usbep_in_progress[ep] = 0; - return 0; + TRACE_ERROR("%s error %s\n", __func__, rc); + usb_buf_free(msg); + bep->in_progress = 0; } return 1; } + +int usb_drain_queue(uint8_t ep) +{ + struct usb_buffered_ep *bep = usb_get_buf_ep(ep); + struct msgb *msg; + int ret = 0; + + /* wait until no transfers are in progress anymore and block + * further interrupts */ + while (1) { + __disable_irq(); + if (!bep->in_progress) { + break; + } + __enable_irq(); + /* retry */ + } + + /* free all queued msgbs */ + while ((msg = msgb_dequeue(&bep->queue))) { + usb_buf_free(msg); + ret++; + } + + /* re-enable interrupts and return number of free'd msgbs */ + __enable_irq(); + + return ret; +} diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c index 13a5b738..db4d1208 100644 --- a/firmware/libcommon/source/mode_cardemu.c +++ b/firmware/libcommon/source/mode_cardemu.c @@ -7,8 +7,9 @@ #include "iso7816_fidi.h" #include "utils.h" #include "osmocom/core/linuxlist.h" +#include "osmocom/core/msgb.h" #include "llist_irqsafe.h" -#include "req_ctx.h" +#include "usb_buf.h" #include "cardemu_prot.h" #define TRACE_ENTRY() TRACE_DEBUG("%s entering\r\n", __func__) @@ -43,7 +44,7 @@ struct cardem_inst { uint32_t vcc_uv_last; }; -static struct cardem_inst cardem_inst[] = { +struct cardem_inst cardem_inst[] = { { .num = 0, .usart_info = { @@ -395,7 +396,7 @@ void mode_cardemu_init(void) PIO_ConfigureIt(&pin_usim1_vcc, usim1_vcc_irqhandler); PIO_EnableIt(&pin_usim1_vcc); #endif /* DETECT_VCC_BY_ADC */ - cardem_inst[0].ch = card_emu_init(0, 2, 0); + cardem_inst[0].ch = card_emu_init(0, 2, 0, PHONE_DATAIN, PHONE_INT); #ifdef CARDEMU_SECOND_UART INIT_LLIST_HEAD(&cardem_inst[1].usb_out_queue); @@ -409,7 +410,7 @@ void mode_cardemu_init(void) PIO_ConfigureIt(&pin_usim2_vcc, usim2_vcc_irqhandler); PIO_EnableIt(&pin_usim2_vcc); #endif /* DETECT_VCC_BY_ADC */ - cardem_inst[1].ch = card_emu_init(1, 0, 1); + cardem_inst[1].ch = card_emu_init(1, 0, 1, CARDEM_USIM2_DATAIN, CARDEM_USIM2_INT); #endif /* CARDEMU_SECOND_UART */ } @@ -419,7 +420,7 @@ void mode_cardemu_exit(void) TRACE_ENTRY(); /* FIXME: stop tc_fdt */ - /* FIXME: release all rctx, unlink them from any queue */ + /* FIXME: release all msg, unlink them from any queue */ PIO_DisableIt(&pin_usim1_rst); PIO_DisableIt(&pin_usim1_vcc); @@ -439,25 +440,24 @@ void mode_cardemu_exit(void) } /* handle a single USB command as received from the USB host */ -static void dispatch_usb_command(struct req_ctx *rctx, struct cardem_inst *ci) +static void dispatch_usb_command(struct msgb *msg, struct cardem_inst *ci) { struct cardemu_usb_msg_hdr *hdr; struct cardemu_usb_msg_set_atr *atr; struct cardemu_usb_msg_cardinsert *cardins; struct llist_head *queue; - hdr = (struct cardemu_usb_msg_hdr *) rctx->data; + hdr = (struct cardemu_usb_msg_hdr *) msg->l1h; switch (hdr->msg_type) { case CEMU_USB_MSGT_DT_TX_DATA: queue = card_emu_get_uart_tx_queue(ci->ch); - req_ctx_set_state(rctx, RCTX_S_UART_TX_PENDING); - llist_add_tail(&rctx->list, queue); + llist_add_tail(&msg->list, queue); card_emu_have_new_uart_tx(ci->ch); break; case CEMU_USB_MSGT_DT_SET_ATR: atr = (struct cardemu_usb_msg_set_atr *) hdr; card_emu_set_atr(ci->ch, atr->atr, atr->atr_len); - req_ctx_put(rctx); + usb_buf_free(msg); break; case CEMU_USB_MSGT_DT_CARDINSERT: cardins = (struct cardemu_usb_msg_cardinsert *) hdr; @@ -467,7 +467,7 @@ static void dispatch_usb_command(struct req_ctx *rctx, struct cardem_inst *ci) PIO_Set(&ci->pin_insert); else PIO_Clear(&ci->pin_insert); - req_ctx_put(rctx); + usb_buf_free(msg); break; case CEMU_USB_MSGT_DT_GET_STATUS: card_emu_report_status(ci->ch); @@ -475,48 +475,56 @@ static void dispatch_usb_command(struct req_ctx *rctx, struct cardem_inst *ci) case CEMU_USB_MSGT_DT_GET_STATS: default: /* FIXME */ - req_ctx_put(rctx); + usb_buf_free(msg); break; } } -static void dispatch_received_rctx(struct req_ctx *rctx, struct cardem_inst *ci) +static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci) { - struct req_ctx *segm; + struct msgb *segm; struct cardemu_usb_msg_hdr *mh; - int i = 0; /* check if we have multiple concatenated commands in * one message. USB endpoints are streams that don't * preserve the message boundaries */ - mh = (struct cardemu_usb_msg_hdr *) rctx->data; - if (mh->msg_len == rctx->tot_len) { + mh = (struct cardemu_usb_msg_hdr *) msg->data; + if (mh->msg_len == msgb_length(msg)) { /* fast path: only one message in buffer */ - dispatch_usb_command(rctx, ci); + dispatch_usb_command(msg, ci); return; } /* slow path: iterate over list of messages, allocating one new * reqe_ctx per segment */ - for (mh = (struct cardemu_usb_msg_hdr *) rctx->data; - (uint8_t *)mh < rctx->data + rctx->tot_len; - mh = (struct cardemu_usb_msg_hdr * ) ((uint8_t *)mh + mh->msg_len)) { - segm = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_MAIN_PROCESSING); + while (1) { + mh = (struct cardemu_usb_msg_hdr *) msg->head; + + segm = usb_buf_alloc(ci->ep_out); if (!segm) { - TRACE_ERROR("%u: ENOMEM during rctx segmentation\r\n", + TRACE_ERROR("%u: ENOMEM during msg segmentation\r\n", ci->num); break; } - segm->idx = 0; - segm->tot_len = mh->msg_len; - memcpy(segm->data, mh, segm->tot_len); - dispatch_usb_command(segm, ci); - i++; + + if (mh->msg_len > msgb_length(msg)) { + TRACE_ERROR("%u: Unexpected large message (%u bytes)\n", + ci->num, mh->msg_len); + usb_buf_free(segm); + } else { + uint8_t *cur = msgb_put(segm, mh->msg_len); + segm->l1h = segm->head; + memcpy(cur, mh, mh->msg_len); + dispatch_usb_command(segm, ci); + } + /* pull this message */ + msgb_pull(msg, mh->msg_len); + /* abort if we're done */ + if (msgb_length(msg) <= 0) + break; } - /* release the master req_ctx, as all segments have been - * processed now */ - req_ctx_put(rctx); + usb_buf_free(msg); } /* iterate over the queue of incoming USB commands and dispatch/execute @@ -525,7 +533,7 @@ static void process_any_usb_commands(struct llist_head *main_q, struct cardem_inst *ci) { struct llist_head *lh; - struct req_ctx *rctx; + struct msgb *msg; int i; /* limit the number of iterations to 10, to ensure we don't get @@ -535,8 +543,8 @@ static void process_any_usb_commands(struct llist_head *main_q, lh = llist_head_dequeue_irqsafe(main_q); if (!lh) break; - rctx = llist_entry(lh, struct req_ctx, list); - dispatch_received_rctx(rctx, ci); + msg = llist_entry(lh, struct msgb, list); + dispatch_received_msg(msg, ci); } } @@ -562,19 +570,16 @@ void mode_cardemu_run(void) //TRACE_ERROR("%uRx%02x\r\n", i, byte); } - queue = card_emu_get_usb_tx_queue(ci->ch); - int usb_pending = llist_count(queue); - if (usb_pending != ci->usb_pending_old) { - TRACE_DEBUG("%u usb_pending=%d\r\n", - i, usb_pending); - ci->usb_pending_old = usb_pending; - } - usb_refill_to_host(queue, ci->ep_in); + /* first try to send any pending messages on IRQ */ + usb_refill_to_host(ci->ep_int); + + /* then try to send any pending messages on IN */ + usb_refill_to_host(ci->ep_in); /* ensure we can handle incoming USB messages from the * host */ - queue = &ci->usb_out_queue; - usb_refill_from_host(queue, ci->ep_out); + usb_refill_from_host(ci->ep_out); + queue = usb_get_queue(ci->ep_out); process_any_usb_commands(queue, ci); } } diff --git a/firmware/libcommon/source/pseudo_talloc.c b/firmware/libcommon/source/pseudo_talloc.c new file mode 100644 index 00000000..f350f7cc --- /dev/null +++ b/firmware/libcommon/source/pseudo_talloc.c @@ -0,0 +1,69 @@ +#include + +#include "talloc.h" +#include "trace.h" +#include "osmocom/core/utils.h" + +#define NUM_RCTX_SMALL 10 +#define RCTX_SIZE_SMALL 348 + +static uint8_t msgb_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL] __attribute__((aligned(sizeof(long)))); +static uint8_t msgb_inuse[NUM_RCTX_SMALL]; + +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + unsigned int i; + + if (size > RCTX_SIZE_SMALL) { + TRACE_ERROR("%s() request too large(%d > %d)\r\n", __func__, size, RCTX_SIZE_SMALL); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(msgb_inuse); i++) { + if (!msgb_inuse[i]) { + uint8_t *out = msgb_data[i]; + msgb_inuse[i] = 1; + memset(out, 0, size); + return out; + } + } + TRACE_ERROR("%s() out of memory!\r\n", __func__); + return NULL; +} + +int _talloc_free(void *ptr, const char *location) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(msgb_inuse); i++) { + if (ptr == msgb_data[i]) { + if (!msgb_inuse[i]) { + TRACE_ERROR("%s: double_free by \r\n", __func__, location); + } else { + msgb_inuse[i] = 0; + } + return 0; + } + } + + TRACE_ERROR("%s: invalid pointer %p from %s\r\n", __func__, ptr, location); + return -1; +} + +void talloc_set_name_const(const void *ptr, const char *name) +{ + /* do nothing */ +} + +#if 0 +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + if (size) + TRACE_ERROR("%s: called with size!=0 from %s\r\n", __func__, name); + return NULL; +} + +void *talloc_pool(const void *context, size_t size) +{ +} +#endif + diff --git a/firmware/libcommon/source/req_ctx.c b/firmware/libcommon/source/req_ctx.c deleted file mode 100644 index 5eb82ea7..00000000 --- a/firmware/libcommon/source/req_ctx.c +++ /dev/null @@ -1,140 +0,0 @@ -/* USB Request Context for OpenPCD / OpenPICC / SIMtrace - * (C) 2006-2016 by Harald Welte - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include - -#include "chip.h" -#include "utils.h" -#include "trace.h" -#include "req_ctx.h" - -#define NUM_RCTX_SMALL 10 -#define NUM_RCTX_LARGE 0 - -#define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE) - -static uint8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL]; -static uint8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE]; - -static struct req_ctx req_ctx[NUM_REQ_CTX]; - -struct req_ctx __ramfunc * -req_ctx_find_get(int large, uint32_t old_state, uint32_t new_state) -{ - struct req_ctx *toReturn = NULL; - unsigned long flags; - unsigned int i; - - if (old_state >= RCTX_STATE_COUNT || new_state >= RCTX_STATE_COUNT) { - TRACE_DEBUG("Invalid parameters for req_ctx_find_get\r\n"); - return NULL; - } - - local_irq_save(flags); - for (i = 0; i < ARRAY_SIZE(req_ctx); i++) { - if (req_ctx[i].state == old_state) { - toReturn = &req_ctx[i]; - toReturn->state = new_state; - break; - } - } - local_irq_restore(flags); - - TRACE_DEBUG("%s(%u, %u, %u)=%p\r\n", __func__, large, old_state, - new_state, toReturn); - - return toReturn; -} - -uint8_t req_ctx_num(struct req_ctx *ctx) -{ - return ctx - req_ctx; -} - -void req_ctx_set_state(struct req_ctx *ctx, uint32_t new_state) -{ - unsigned long flags; - - TRACE_DEBUG("%s(ctx=%p, new_state=%u)\r\n", __func__, ctx, new_state); - - if (new_state >= RCTX_STATE_COUNT) { - TRACE_DEBUG("Invalid new_state for req_ctx_set_state\r\n"); - return; - } - local_irq_save(flags); - ctx->state = new_state; - local_irq_restore(flags); -} - -#ifdef DEBUG_REQCTX -void req_print(int state) { - int count = 0; - struct req_ctx *ctx, *last = NULL; - DEBUGP("State [%02i] start <==> ", state); - ctx = req_ctx_queues[state]; - while (ctx) { - if (last != ctx->prev) - DEBUGP("*INV_PREV* "); - DEBUGP("%08X => ", ctx); - last = ctx; - ctx = ctx->next; - count++; - if (count > NUM_REQ_CTX) { - DEBUGP("*WILD POINTER* => "); - break; - } - } - TRACE_DEBUG("NULL"); - if (!req_ctx_queues[state] && req_ctx_tails[state]) { - TRACE_DEBUG("NULL head, NON-NULL tail\r\n"); - } - if (last != req_ctx_tails[state]) { - TRACE_DEBUG("Tail does not match last element\r\n"); - } -} -#endif - -void req_ctx_put(struct req_ctx *ctx) -{ - return req_ctx_set_state(ctx, RCTX_S_FREE); -} - -void req_ctx_init(void) -{ - int i; - for (i = 0; i < NUM_RCTX_SMALL; i++) { - req_ctx[i].size = RCTX_SIZE_SMALL; - req_ctx[i].tot_len = 0; - req_ctx[i].data = rctx_data[i]; - req_ctx[i].state = RCTX_S_FREE; - TRACE_DEBUG("SMALL req_ctx[%02i] initialized at %p, Data: %p => %p\r\n", - i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_SMALL); - } - - for (; i < NUM_REQ_CTX; i++) { - req_ctx[i].size = RCTX_SIZE_LARGE; - req_ctx[i].tot_len = 0; - req_ctx[i].data = rctx_data_large[i]; - req_ctx[i].state = RCTX_S_FREE; - TRACE_DEBUG("LARGE req_ctx[%02i] initialized at %p, Data: %p => %p\r\n", - i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_LARGE); - } -} diff --git a/firmware/libcommon/source/usb_buf.c b/firmware/libcommon/source/usb_buf.c new file mode 100644 index 00000000..ddeb43bd --- /dev/null +++ b/firmware/libcommon/source/usb_buf.c @@ -0,0 +1,76 @@ +#include "board.h" +#include "trace.h" +#include "usb_buf.h" + +#include "osmocom/core/linuxlist.h" +#include "osmocom/core/msgb.h" +#include + +#define USB_ALLOC_SIZE 280 + +static struct usb_buffered_ep usb_buffered_ep[BOARD_USB_NUMENDPOINTS]; + +struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep) +{ + if (ep >= ARRAY_SIZE(usb_buffered_ep)) + return NULL; + return &usb_buffered_ep[ep]; +} + + +/*********************************************************************** + * User API + ***********************************************************************/ + +struct llist_head *usb_get_queue(uint8_t ep) +{ + struct usb_buffered_ep *bep = usb_get_buf_ep(ep); + if (!bep) + return NULL; + return &bep->queue; +} + +/* allocate a USB buffer for use with given end-point */ +struct msgb *usb_buf_alloc(uint8_t ep) +{ + struct msgb *msg; + + msg = msgb_alloc(USB_ALLOC_SIZE, "USB"); + if (!msg) + return NULL; + msg->dst = usb_get_buf_ep(ep); + return msg; +} + +/* release/return the USB buffer to the pool */ +void usb_buf_free(struct msgb *msg) +{ + msgb_free(msg); +} + +/* submit a USB buffer for transmission to host */ +int usb_buf_submit(struct msgb *msg) +{ + struct usb_buffered_ep *ep = msg->dst; + + if (!msg->dst) { + TRACE_ERROR("%s: msg without dst\r\n", __func__); + usb_buf_free(msg); + return -EINVAL; + } + + /* no need for irqsafe operation, as the usb_tx_queue is + * processed only by the main loop context */ + msgb_enqueue(&ep->queue, msg); + return 0; +} + +void usb_buf_init(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(usb_buffered_ep); i++) { + struct usb_buffered_ep *ep = &usb_buffered_ep[i]; + INIT_LLIST_HEAD(&ep->queue); + } +} diff --git a/firmware/test/Makefile b/firmware/test/Makefile index 37f42406..7384033a 100644 --- a/firmware/test/Makefile +++ b/firmware/test/Makefile @@ -1,8 +1,9 @@ CFLAGS=-g -Wall -I../src_simtrace -I../libcommon/include -I. +LDFLAGS=-losmocore VPATH=../src_simtrace ../libcommon/source -card_emu_test: card_emu_tests.hobj card_emu.hobj req_ctx.hobj iso7816_fidi.hobj +card_emu_test: card_emu_tests.hobj card_emu.hobj usb_buf.hobj iso7816_fidi.hobj $(CC) $(LDFLAGS) -o $@ $^ %.hobj: %.c diff --git a/firmware/test/card_emu_tests.c b/firmware/test/card_emu_tests.c index 8cc78247..75a8a993 100644 --- a/firmware/test/card_emu_tests.c +++ b/firmware/test/card_emu_tests.c @@ -7,7 +7,11 @@ #include "card_emu.h" #include "cardemu_prot.h" #include "tc_etu.h" -#include "req_ctx.h" +#include "usb_buf.h" + +#define PHONE_DATAIN 1 +#define PHONE_INT 2 +#define PHONE_DATAOUT 3 /* stub functions required by card_emu.c */ @@ -128,17 +132,20 @@ static void reader_send_bytes(struct card_handle *ch, const uint8_t *bytes, unsi } } -static void dump_rctx(struct req_ctx *rctx) +static void dump_rctx(struct msgb *msg) { struct cardemu_usb_msg_hdr *mh = - (struct cardemu_usb_msg_hdr *) rctx->data; + (struct cardemu_usb_msg_hdr *) msg->l1h; struct cardemu_usb_msg_rx_data *rxd; int i; +#if 0 printf("req_ctx(%p): state=%u, size=%u, tot_len=%u, idx=%u, data=%p\n", rctx, rctx->state, rctx->size, rctx->tot_len, rctx->idx, rctx->data); printf(" msg_type=%u, seq_nr=%u, msg_len=%u\n", mh->msg_type, mh->seq_nr, mh->msg_len); +#endif + printf("%s\n", msgb_hexdump(msg)); switch (mh->msg_type) { case CEMU_USB_MSGT_DO_RX_DATA: @@ -151,23 +158,28 @@ static void dump_rctx(struct req_ctx *rctx) } } -static void get_and_verify_rctx(int state, const uint8_t *data, unsigned int len) +static void get_and_verify_rctx(uint8_t ep, const uint8_t *data, unsigned int len) { - struct req_ctx *rctx; + struct llist_head *queue = usb_get_queue(ep); + struct msgb *msg; struct cardemu_usb_msg_tx_data *td; struct cardemu_usb_msg_rx_data *rd; + struct cardemu_usb_msg_hdr *mh; - rctx = req_ctx_find_get(0, state, RCTX_S_USB_TX_BUSY); - assert(rctx); - dump_rctx(rctx); + assert(queue); + msg = msgb_dequeue(queue); + assert(msg); + dump_rctx(msg); + assert(msg->l1h); + mh = (struct cardemu_usb_msg_hdr *) msg->l1h; /* verify the contents of the rctx */ - switch (state) { - case RCTX_S_USB_TX_PENDING: - td = (struct cardemu_usb_msg_tx_data *) rctx->data; - assert(td->hdr.msg_type == CEMU_USB_MSGT_DO_RX_DATA); - assert(td->data_len == len); - assert(!memcmp(td->data, data, len)); + switch (mh->msg_type) { + case CEMU_USB_MSGT_DO_RX_DATA: + rd = (struct cardemu_usb_msg_rx_data *) msg->l1h; + assert(rd->hdr.msg_type == CEMU_USB_MSGT_DO_RX_DATA); + assert(rd->data_len == len); + assert(!memcmp(rd->data, data, len)); break; #if 0 case RCTX_S_UART_RX_PENDING: @@ -181,55 +193,62 @@ static void get_and_verify_rctx(int state, const uint8_t *data, unsigned int len } /* free the req_ctx, indicating it has fully arrived on the host */ - req_ctx_set_state(rctx, RCTX_S_FREE); + usb_buf_free(msg); } static void get_and_verify_rctx_pps(const uint8_t *data, unsigned int len) { - struct req_ctx *rctx; + struct llist_head *queue = usb_get_queue(PHONE_DATAIN); + struct msgb *msg; struct cardemu_usb_msg_pts_info *ptsi; - rctx = req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY); - assert(rctx); - dump_rctx(rctx); + assert(queue); + msg = msgb_dequeue(queue); + assert(msg); + dump_rctx(msg); + assert(msg->l1h); - ptsi = (struct cardemu_usb_msg_pts_info *) rctx->data; + ptsi = (struct cardemu_usb_msg_pts_info *) msg->l1h; /* FIXME: verify */ assert(ptsi->hdr.msg_type == CEMU_USB_MSGT_DO_PTS); assert(!memcmp(ptsi->req, data, len)); assert(!memcmp(ptsi->resp, data, len)); /* free the req_ctx, indicating it has fully arrived on the host */ - req_ctx_set_state(rctx, RCTX_S_FREE); + usb_buf_free(msg); } /* emulate a TPDU header being sent by the reader/phone */ static void rdr_send_tpdu_hdr(struct card_handle *ch, const uint8_t *tpdu_hdr) { + struct llist_head *queue = usb_get_queue(PHONE_DATAIN); + /* we don't want a receive context to become available during * the first four bytes */ reader_send_bytes(ch, tpdu_hdr, 4); - assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); + assert(llist_empty(queue)); reader_send_bytes(ch, tpdu_hdr+4, 1); /* but then after the final byte of the TPDU header, we want a * receive context to be available for USB transmission */ - get_and_verify_rctx(RCTX_S_USB_TX_PENDING, tpdu_hdr, 5); + get_and_verify_rctx(PHONE_DATAIN, tpdu_hdr, 5); } /* emulate a CEMU_USB_MSGT_DT_TX_DATA received from USB */ -static void host_to_device_data(const uint8_t *data, uint16_t len, unsigned int flags) +static void host_to_device_data(struct card_handle *ch, const uint8_t *data, uint16_t len, + unsigned int flags) { - struct req_ctx *rctx; + struct msgb *msg; struct cardemu_usb_msg_tx_data *rd; + struct llist_head *queue; /* allocate a free req_ctx */ - rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_USB_RX_BUSY); - assert(rctx); + msg = usb_buf_alloc(PHONE_DATAOUT); + assert(msg); + msg->l1h = msg->head; /* initialize the header */ - rd = (struct cardemu_usb_msg_tx_data *) rctx->data; - rctx->tot_len = sizeof(*rd) + len; + rd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*rd) + len); cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DT_TX_DATA); rd->flags = flags; /* copy data and set length */ @@ -238,7 +257,9 @@ static void host_to_device_data(const uint8_t *data, uint16_t len, unsigned int rd->hdr.msg_len = sizeof(*rd) + len; /* hand the req_ctx to the UART transmit code */ - req_ctx_set_state(rctx, RCTX_S_UART_TX_PENDING); + queue = card_emu_get_uart_tx_queue(ch); + assert(queue); + msgb_enqueue(queue, msg); } /* card-transmit any pending characters */ @@ -270,7 +291,7 @@ test_tpdu_reader2card(struct card_handle *ch, const uint8_t *hdr, const uint8_t card_tx_verify_chars(ch, NULL, 0); /* card emulator PC sends a singly byte PB response via USB */ - host_to_device_data(hdr+1, 1, CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_RX); + host_to_device_data(ch, hdr+1, 1, CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_RX); /* card actually sends that single PB */ card_tx_verify_chars(ch, hdr+1, 1); @@ -278,13 +299,13 @@ test_tpdu_reader2card(struct card_handle *ch, const uint8_t *hdr, const uint8_t reader_send_bytes(ch, body, body_len); /* check if we have received them on the USB side */ - get_and_verify_rctx(RCTX_S_USB_TX_PENDING, body, body_len); + get_and_verify_rctx(PHONE_DATAIN, body, body_len); /* ensure there is no extra data received on usb */ - assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); + assert(llist_empty(usb_get_queue(PHONE_DATAOUT))); /* card emulator sends SW via USB */ - host_to_device_data(tpdu_pb_sw, sizeof(tpdu_pb_sw), + host_to_device_data(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw), CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_TX); /* obtain any pending tx chars */ card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw)); @@ -304,21 +325,21 @@ test_tpdu_card2reader(struct card_handle *ch, const uint8_t *hdr, const uint8_t card_tx_verify_chars(ch, NULL, 0); /* card emulator PC sends a response PB via USB */ - host_to_device_data(hdr+1, 1, CEMU_DATA_F_PB_AND_TX); + host_to_device_data(ch, hdr+1, 1, CEMU_DATA_F_PB_AND_TX); /* card actually sends that PB */ card_tx_verify_chars(ch, hdr+1, 1); /* emulate more characters from card to reader */ - host_to_device_data(body, body_len, 0); + host_to_device_data(ch, body, body_len, 0); /* obtain those bytes as they arrvive on the card */ card_tx_verify_chars(ch, body, body_len); /* ensure there is no extra data received on usb */ - assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); + assert(llist_empty(usb_get_queue(PHONE_DATAOUT))); /* card emulator sends SW via USB */ - host_to_device_data(tpdu_pb_sw, sizeof(tpdu_pb_sw), CEMU_DATA_F_FINAL); + host_to_device_data(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw), CEMU_DATA_F_FINAL); /* obtain any pending tx chars */ card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw)); @@ -363,11 +384,11 @@ int main(int argc, char **argv) struct card_handle *ch; unsigned int i; - req_ctx_init(); - - ch = card_emu_init(0, 23, 42); + ch = card_emu_init(0, 23, 42, PHONE_DATAIN, PHONE_INT); assert(ch); + usb_buf_init(); + /* start up the card (VCC/RST, ATR) */ io_start_card(ch); card_tx_verify_chars(ch, NULL, 0);