It's incompatible because due to the way the USB peripheral transfer the data out to the host, there is NO mechanism to indicate exactly where the next USB transaction should start. Therefore, when the data is flowing very fast, every 64 byte (the bus width) write to the USB peripheral will be concatenated together, resulting in the host read to combine multiple req_ctx buffers.
The way I solved it is by adding fields to the simtrace_hdr structure: (the + indicates the new fields). This allows me on the host to keep track if / where a new req_ctx begins in the received usb data.
struct simtrace_hdr {
u_int8_t cmd;
u_int8_t flags;
u_int8_t res[2];
+ u_int16_t seq_num;
+ u_int16_t offset;
+ u_int16_t tot_len;
u_int8_t data[0];
} __attribute__ ((packed));
Since the laptop that I was using to create multiple commits is not available at the moment, I am attaching the git diff from the repository (instead of from my last commits in previous email). Meaning this is a single diff file.
For the host software, the basic change in the process_usb_msg function. My implementation is a naive implementation using a much larger buffer to copy data to, where if a req_ctx is split across multiple usb transfers, it will join those fragments then process:
struct simtrace_hdr {
u_int8_t cmd;
u_int8_t flags;
u_int8_t res[2];
u_int16_t seq_num;
u_int16_t offset;
u_int16_t tot_len;
u_int8_t data[0];
} /* __attribute__ ((packed)) */;
static char usb_buffer[66000];
static unsigned usb_remainder = 0;
int process_usb_msg(uint8_t *buf, int len) {
struct simtrace_hdr *sh;
uint8_t *payload;
int payload_len, sh_offset, i;
sendto(s, buf, len, 0, (struct sockaddr*)&destUSB, sizeof(struct sockaddr_in));
memcpy(usb_buffer + usb_remainder, buf, len);
len += usb_remainder;
usb_remainder = len;
sh_offset = 0;
sh = usb_buffer;
payload = (char*)sh + sizeof(struct simtrace_hdr);
payload_len = sh->tot_len - sizeof(struct simtrace_hdr);
if (usb_remainder < sh->tot_len)
sh = NULL;
while (sh) {
switch (sh->cmd) {
case SIMTRACE_MSGT_DATA:
/* special treatment for ATR */
if (sh->flags & SIMTRACE_FLAG_ATR) {
apdu_split_reset(as);
break;
}
if (sh->flags & SIMTRACE_FLAG_PPS_FIDI) {
module_out("PPS(Fi=%u/Di=%u)\n",
sh->res[0], sh->res[1]);
}
/* everything else goes into APDU splitter */
apdu_split_in(as, payload, payload_len);
#if 0
/* If waiting time has expired, signal explicit boundary */
if (sh->flags & SIMTRACE_FLAG_WTIME_EXP)
apdu_split_boundary(as);
#endif
break;
case SIMTRACE_MSGT_RESET:
default:
module_out("unknown simtrace msg type 0x%02x\n", sh->cmd);
break;
}
sh_offset += sh->tot_len;
sh = usb_buffer + sh_offset;
if (sh_offset < len) {
if (sh_offset + sh->tot_len > len) {
memcpy(usb_buffer, sh, len - sh_offset);
usb_remainder = len - sh_offset;
sh = NULL;
} else {
payload = (char*)sh + sizeof(struct simtrace_hdr);
payload_len = sh->tot_len - sizeof(struct simtrace_hdr);
}
} else {
sh = NULL;
usb_remainder = 0;
}
}
return 0;
}