mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-20 07:18:33 +03:00
firmware: More common infrastructure for USB handling
Let's move low-level handling of endpoint queue refill from the individual apps into common/shared code. Now the main simply has to call usb_process() for every interface, and inbound messages will be dispatched to call-back functions from there. Change-Id: Ic6f9c6c1ffdbb0c9c3b284371ecc83b17e3be746
This commit is contained in:
@@ -221,6 +221,7 @@ extern int main(void)
|
|||||||
}
|
}
|
||||||
last_simtrace_config = simtrace_config;
|
last_simtrace_config = simtrace_config;
|
||||||
} else {
|
} else {
|
||||||
|
//FIXME: usb_proces() for every interface in this configuration?
|
||||||
if (config_func_ptrs[simtrace_config].run) {
|
if (config_func_ptrs[simtrace_config].run) {
|
||||||
config_func_ptrs[simtrace_config].run();
|
config_func_ptrs[simtrace_config].run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,15 @@ int usb_drain_queue(uint8_t ep);
|
|||||||
void usb_buf_init(void);
|
void usb_buf_init(void);
|
||||||
struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep);
|
struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep);
|
||||||
|
|
||||||
int usb_refill_to_host(uint8_t ep);
|
struct usb_if {
|
||||||
int usb_refill_from_host(uint8_t ep);
|
uint8_t if_num; /* interface number */
|
||||||
|
uint8_t ep_out; /* OUT endpoint (0 if none) */
|
||||||
|
uint8_t ep_in; /* IN endpint (0 if none) */
|
||||||
|
uint8_t ep_int; /* INT endpoint (0 if none) */
|
||||||
|
void *data; /* opaque data, passed through */
|
||||||
|
struct {
|
||||||
|
/* call-back to be called for inclming messages on OUT EP */
|
||||||
|
void (*rx_out)(struct msgb *msg, const struct usb_if *usb_if);
|
||||||
|
} ops;
|
||||||
|
};
|
||||||
|
void usb_process(const struct usb_if *usb_if);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check if the spcified IN endpoint is idle and submit the next buffer from queue */
|
/* check if the spcified IN endpoint is idle and submit the next buffer from queue */
|
||||||
int usb_refill_to_host(uint8_t ep)
|
static int usb_refill_to_host(uint8_t ep)
|
||||||
{
|
{
|
||||||
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
@@ -130,7 +130,7 @@ static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* refill the read queue for data received from host PC on OUT EP, if needed */
|
/* refill the read queue for data received from host PC on OUT EP, if needed */
|
||||||
int usb_refill_from_host(uint8_t ep)
|
static int usb_refill_from_host(uint8_t ep)
|
||||||
{
|
{
|
||||||
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
@@ -198,3 +198,45 @@ int usb_drain_queue(uint8_t ep)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* iterate over the queue of incoming USB commands and dispatch/execute
|
||||||
|
* them */
|
||||||
|
static void process_any_usb_commands(const struct usb_if *usb_if)
|
||||||
|
{
|
||||||
|
struct llist_head *queue = usb_get_queue(usb_if->ep_out);
|
||||||
|
struct llist_head *lh;
|
||||||
|
struct msgb *msg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* limit the number of iterations to 10, to ensure we don't get
|
||||||
|
* stuck here without returning to main loop processing */
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
/* de-queue the list head in an irq-safe way */
|
||||||
|
lh = llist_head_dequeue_irqsafe(queue);
|
||||||
|
if (!lh)
|
||||||
|
break;
|
||||||
|
msg = llist_entry(lh, struct msgb, list);
|
||||||
|
usb_if->ops.rx_out(msg, usb_if);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* perform any action related to USB processing (IRQ/INT/OUT EP refill, handling OUT) */
|
||||||
|
void usb_process(const struct usb_if *usb_if)
|
||||||
|
{
|
||||||
|
/* first try to send any pending messages on IRQ */
|
||||||
|
if (usb_if->ep_int)
|
||||||
|
usb_refill_to_host(usb_if->ep_int);
|
||||||
|
|
||||||
|
/* then try to send any pending messages on IN */
|
||||||
|
if (usb_if->ep_in)
|
||||||
|
usb_refill_to_host(usb_if->ep_in);
|
||||||
|
|
||||||
|
/* ensure we can handle incoming USB messages from the
|
||||||
|
* host */
|
||||||
|
if (usb_if->ep_out) {
|
||||||
|
usb_refill_from_host(usb_if->ep_out);
|
||||||
|
process_any_usb_commands(usb_if);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
#define TRACE_ENTRY() TRACE_DEBUG("%s entering\r\n", __func__)
|
#define TRACE_ENTRY() TRACE_DEBUG("%s entering\r\n", __func__)
|
||||||
|
|
||||||
|
static void dispatch_received_usb_msg(struct msgb *msg, const struct usb_if *usb_if);
|
||||||
|
|
||||||
#ifdef PINS_CARDSIM
|
#ifdef PINS_CARDSIM
|
||||||
static const Pin pins_cardsim[] = PINS_CARDSIM;
|
static const Pin pins_cardsim[] = PINS_CARDSIM;
|
||||||
#endif
|
#endif
|
||||||
@@ -68,9 +70,7 @@ struct cardem_inst {
|
|||||||
bool half_time_notified;
|
bool half_time_notified;
|
||||||
} wt;
|
} wt;
|
||||||
int usb_pending_old;
|
int usb_pending_old;
|
||||||
uint8_t ep_out;
|
struct usb_if usb_if;
|
||||||
uint8_t ep_in;
|
|
||||||
uint8_t ep_int;
|
|
||||||
const Pin pin_insert;
|
const Pin pin_insert;
|
||||||
#ifdef DETECT_VCC_BY_ADC
|
#ifdef DETECT_VCC_BY_ADC
|
||||||
uint32_t vcc_uv;
|
uint32_t vcc_uv;
|
||||||
@@ -90,9 +90,16 @@ struct cardem_inst cardem_inst[] = {
|
|||||||
.id = ID_USART1,
|
.id = ID_USART1,
|
||||||
.state = USART_RCV
|
.state = USART_RCV
|
||||||
},
|
},
|
||||||
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM1_DATAOUT,
|
.usb_if = {
|
||||||
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
|
.if_num = 0,
|
||||||
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM1_INT,
|
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM1_DATAOUT,
|
||||||
|
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN,
|
||||||
|
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM1_INT,
|
||||||
|
.data = &cardem_inst[0],
|
||||||
|
.ops = {
|
||||||
|
.rx_out = dispatch_received_usb_msg,
|
||||||
|
},
|
||||||
|
},
|
||||||
#ifdef PIN_SET_USIM1_PRES
|
#ifdef PIN_SET_USIM1_PRES
|
||||||
.pin_insert = PIN_SET_USIM1_PRES,
|
.pin_insert = PIN_SET_USIM1_PRES,
|
||||||
#endif
|
#endif
|
||||||
@@ -105,9 +112,16 @@ struct cardem_inst cardem_inst[] = {
|
|||||||
.id = ID_USART0,
|
.id = ID_USART0,
|
||||||
.state = USART_RCV
|
.state = USART_RCV
|
||||||
},
|
},
|
||||||
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM2_DATAOUT,
|
.usb_if = {
|
||||||
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
|
.if_num = 1,
|
||||||
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM2_INT,
|
.ep_out = SIMTRACE_CARDEM_USB_EP_USIM2_DATAOUT,
|
||||||
|
.ep_in = SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN,
|
||||||
|
.ep_int = SIMTRACE_CARDEM_USB_EP_USIM2_INT,
|
||||||
|
.data = &cardem_inst[1],
|
||||||
|
.ops = {
|
||||||
|
.rx_out = dispatch_received_usb_msg,
|
||||||
|
},
|
||||||
|
},
|
||||||
#ifdef PIN_SET_USIM2_PRES
|
#ifdef PIN_SET_USIM2_PRES
|
||||||
.pin_insert = PIN_SET_USIM2_PRES,
|
.pin_insert = PIN_SET_USIM2_PRES,
|
||||||
#endif
|
#endif
|
||||||
@@ -787,8 +801,9 @@ static void dispatch_usb_command_modem(struct msgb *msg, struct cardem_inst *ci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* handle a single USB command as received from the USB host */
|
/* handle a single USB command as received from the USB host */
|
||||||
static void dispatch_usb_command(struct msgb *msg, struct cardem_inst *ci)
|
static void dispatch_usb_command(struct msgb *msg, const struct usb_if *usb_if)
|
||||||
{
|
{
|
||||||
|
struct cardem_inst *ci = usb_if->data;
|
||||||
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h;
|
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h;
|
||||||
|
|
||||||
if (msgb_length(msg) < sizeof(*sh)) {
|
if (msgb_length(msg) < sizeof(*sh)) {
|
||||||
@@ -817,7 +832,8 @@ static void dispatch_usb_command(struct msgb *msg, struct cardem_inst *ci)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci)
|
/* handle a single USB transfer as received from the USB host */
|
||||||
|
static void dispatch_received_usb_msg(struct msgb *msg, const struct usb_if *usb_if)
|
||||||
{
|
{
|
||||||
struct msgb *segm;
|
struct msgb *segm;
|
||||||
struct simtrace_msg_hdr *mh;
|
struct simtrace_msg_hdr *mh;
|
||||||
@@ -828,7 +844,7 @@ static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci)
|
|||||||
mh = (struct simtrace_msg_hdr *) msg->data;
|
mh = (struct simtrace_msg_hdr *) msg->data;
|
||||||
if (mh->msg_len == msgb_length(msg)) {
|
if (mh->msg_len == msgb_length(msg)) {
|
||||||
/* fast path: only one message in buffer */
|
/* fast path: only one message in buffer */
|
||||||
dispatch_usb_command(msg, ci);
|
dispatch_usb_command(msg, usb_if);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,23 +853,23 @@ static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci)
|
|||||||
while (1) {
|
while (1) {
|
||||||
mh = (struct simtrace_msg_hdr *) msg->data;
|
mh = (struct simtrace_msg_hdr *) msg->data;
|
||||||
|
|
||||||
segm = usb_buf_alloc(ci->ep_out);
|
segm = usb_buf_alloc(usb_if->ep_out);
|
||||||
if (!segm) {
|
if (!segm) {
|
||||||
TRACE_ERROR("%u: ENOMEM during msg segmentation\r\n",
|
TRACE_ERROR("%u: ENOMEM during msg segmentation\r\n",
|
||||||
ci->num);
|
usb_if->if_num);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mh->msg_len > msgb_length(msg)) {
|
if (mh->msg_len > msgb_length(msg)) {
|
||||||
TRACE_ERROR("%u: Unexpected large message (%u bytes)\r\n",
|
TRACE_ERROR("%u: Unexpected large message (%u bytes)\r\n",
|
||||||
ci->num, mh->msg_len);
|
usb_if->if_num, mh->msg_len);
|
||||||
usb_buf_free(segm);
|
usb_buf_free(segm);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
uint8_t *cur = msgb_put(segm, mh->msg_len);
|
uint8_t *cur = msgb_put(segm, mh->msg_len);
|
||||||
segm->l1h = segm->head;
|
segm->l1h = segm->head;
|
||||||
memcpy(cur, mh, mh->msg_len);
|
memcpy(cur, mh, mh->msg_len);
|
||||||
dispatch_usb_command(segm, ci);
|
dispatch_usb_command(segm, usb_if);
|
||||||
}
|
}
|
||||||
/* pull this message */
|
/* pull this message */
|
||||||
msgb_pull(msg, mh->msg_len);
|
msgb_pull(msg, mh->msg_len);
|
||||||
@@ -865,35 +881,14 @@ static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci)
|
|||||||
usb_buf_free(msg);
|
usb_buf_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* iterate over the queue of incoming USB commands and dispatch/execute
|
|
||||||
* them */
|
|
||||||
static void process_any_usb_commands(struct llist_head *main_q,
|
|
||||||
struct cardem_inst *ci)
|
|
||||||
{
|
|
||||||
struct llist_head *lh;
|
|
||||||
struct msgb *msg;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* limit the number of iterations to 10, to ensure we don't get
|
|
||||||
* stuck here without returning to main loop processing */
|
|
||||||
for (i = 0; i < 10; i++) {
|
|
||||||
/* de-queue the list head in an irq-safe way */
|
|
||||||
lh = llist_head_dequeue_irqsafe(main_q);
|
|
||||||
if (!lh)
|
|
||||||
break;
|
|
||||||
msg = llist_entry(lh, struct msgb, list);
|
|
||||||
dispatch_received_msg(msg, ci);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* main loop function, called repeatedly */
|
/* main loop function, called repeatedly */
|
||||||
void mode_cardemu_run(void)
|
void mode_cardemu_run(void)
|
||||||
{
|
{
|
||||||
struct llist_head *queue;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(cardem_inst); i++) {
|
for (i = 0; i < ARRAY_SIZE(cardem_inst); i++) {
|
||||||
struct cardem_inst *ci = &cardem_inst[i];
|
struct cardem_inst *ci = &cardem_inst[i];
|
||||||
|
struct usb_if *usb_if = &ci->usb_if;
|
||||||
|
|
||||||
/* drain the ring buffer from UART into card_emu */
|
/* drain the ring buffer from UART into card_emu */
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -910,16 +905,6 @@ void mode_cardemu_run(void)
|
|||||||
|
|
||||||
process_io_statechg(ci);
|
process_io_statechg(ci);
|
||||||
|
|
||||||
/* first try to send any pending messages on IRQ */
|
usb_process(&ci->usb_if);
|
||||||
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 */
|
|
||||||
usb_refill_from_host(ci->ep_out);
|
|
||||||
queue = usb_get_queue(ci->ep_out);
|
|
||||||
process_any_usb_commands(queue, ci);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -975,20 +975,28 @@ static void usb_send_change(uint32_t flags)
|
|||||||
usb_msg_upd_len_and_submit(usb_msg);
|
usb_msg_upd_len_and_submit(usb_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* handle incoming message from USB OUT EP */
|
||||||
|
static void dispatch_usb_out(struct msgb *msg, const struct usb_if *usb_if)
|
||||||
|
{
|
||||||
|
/* currently we don't need any incoming data */
|
||||||
|
msgb_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct usb_if sniffer_usb_if = {
|
||||||
|
.if_num = 0,
|
||||||
|
.ep_in = SIMTRACE_USB_EP_CARD_DATAIN,
|
||||||
|
.ep_int = SIMTRACE_USB_EP_CARD_INT,
|
||||||
|
.ep_out = SIMTRACE_USB_EP_CARD_DATAOUT,
|
||||||
|
.ops = {
|
||||||
|
.rx_out = dispatch_usb_out,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* Main (idle/busy) loop of this USB configuration */
|
/* Main (idle/busy) loop of this USB configuration */
|
||||||
void Sniffer_run(void)
|
void Sniffer_run(void)
|
||||||
{
|
{
|
||||||
/* Handle USB queue */
|
/* Handle USB queue */
|
||||||
/* first try to send any pending messages on INT */
|
usb_process(&sniffer_usb_if);
|
||||||
usb_refill_to_host(SIMTRACE_USB_EP_CARD_INT);
|
|
||||||
/* then try to send any pending messages on IN */
|
|
||||||
usb_refill_to_host(SIMTRACE_USB_EP_CARD_DATAIN);
|
|
||||||
/* ensure we can handle incoming USB messages from the host */
|
|
||||||
/* currently we don't need any incoming data
|
|
||||||
usb_refill_from_host(SIMTRACE_USB_EP_CARD_DATAOUT);
|
|
||||||
struct llist_head *queue = usb_get_queue(SIMTRACE_USB_EP_CARD_DATAOUT);
|
|
||||||
process_any_usb_commands(queue);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* WARNING: the signal data and flags are not synchronized. We have to hope
|
/* WARNING: the signal data and flags are not synchronized. We have to hope
|
||||||
* the processing is fast enough to not land in the wrong state while data
|
* the processing is fast enough to not land in the wrong state while data
|
||||||
|
|||||||
Reference in New Issue
Block a user