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.
diff --git a/firmware/Makefile b/firmware/Makefile
index a933102..50cc2dd 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -97,7 +97,7 @@
 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 8a2f29b..8129b87 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 7d90eb0..9adda56 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 @@
 	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 2b44f01..5f683ce 100644
--- a/firmware/apps/dfu/main.c
+++ b/firmware/apps/dfu/main.c
@@ -190,8 +190,6 @@
 	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 8a2f29b..8129b87 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 9f25a4b..24d5649 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_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 df23ce4..43279f8 100644
--- a/firmware/libcommon/include/card_emu.h
+++ b/firmware/libcommon/include/card_emu.h
@@ -10,7 +10,8 @@
 	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 @@
 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 2a54d76..b9589f3 100644
--- a/firmware/libcommon/include/simtrace.h
+++ b/firmware/libcommon/include/simtrace.h
@@ -113,8 +113,4 @@
 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 0000000..f2d9666
--- /dev/null
+++ b/firmware/libcommon/include/talloc.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+/* 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 0000000..dd16531
--- /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 a1be34c..6edf05e 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 <hwelte@hmw-consulting.de>
+/* (C) 2010-2017 by Harald Welte <hwelte@hmw-consulting.de>
  *
  *  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 @@
 	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 @@
 		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 @@
 	} 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 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 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 @@
 /* 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 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 @@
 			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 @@
 /* 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 @@
 	}
 
 	/* 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 @@
 				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_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 @@
 	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 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 @@
 
 	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 816c7a1..acf931e 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 <errno.h>
 
-#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;
+	bep->in_progress = 1;
 
-	rc = USBD_Read(ep, rctx->data, rctx->size,
-			(TransferCallback) &usb_read_cb, rctx);
-
+	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 13a5b73..db4d120 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 @@
 	uint32_t vcc_uv_last;
 };
 
-static struct cardem_inst cardem_inst[] = {
+struct cardem_inst cardem_inst[] = {
 	{
 		.num = 0,
 		.usart_info = 	{
@@ -395,7 +396,7 @@
 	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 @@
 	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 @@
 	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 @@
 }
 
 /* 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 @@
 			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 @@
 	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 @@
 				     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 @@
 		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 @@
 			//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 0000000..f350f7c
--- /dev/null
+++ b/firmware/libcommon/source/pseudo_talloc.c
@@ -0,0 +1,69 @@
+#include <stdint.h>
+
+#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 5eb82ea..0000000
--- 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 <hwelte@hmw-consulting.de>
- *
- *  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 <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#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 0000000..ddeb43b
--- /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 <errno.h>
+
+#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 37f4240..7384033 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 8cc7824..75a8a99 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 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 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 @@
 	}
 
 	/* 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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);