summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2012-08-26 18:47:42 +0200
committerAlon Levy <alevy@redhat.com>2012-08-26 18:48:28 +0200
commit8299050506bc4fc5aaa3be6d4eecab6e14ae0061 (patch)
tree0c7d3a8cef09abfc240a8bff0f4d08463ea159fe
parent768aa6172db18fd6493abad77f743b7fc40108ed (diff)
ccid-card-passthru: handle partial writes to chardevqxl/pull
Signed-off-by: Alon Levy <alevy@redhat.com>
-rw-r--r--hw/ccid-card-passthru.c152
1 files changed, 129 insertions, 23 deletions
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
index bd6c77777..906afe931 100644
--- a/hw/ccid-card-passthru.c
+++ b/hw/ccid-card-passthru.c
@@ -42,23 +42,74 @@ uint8_t DEFAULT_ATR[] = {
#define PASSTHRU_DEV_NAME "ccid-card-passthru"
#define VSCARD_IN_SIZE 65536
+/* 10 Hz polling of chardev out */
+#define VSCARD_OUT_TIMER_DELTA_MS 100
+
/* maximum size of ATR - from 7816-3 */
#define MAX_ATR_SIZE 40
+typedef struct Buffer {
+ uint32_t pos;
+ uint32_t size;
+ uint32_t max_size;
+ uint32_t resize;
+ uint8_t *data;
+} Buffer;
+
typedef struct PassthruState PassthruState;
struct PassthruState {
CCIDCardState base;
CharDriverState *cs;
- uint8_t vscard_in_data[VSCARD_IN_SIZE];
- uint32_t vscard_in_pos;
+ Buffer vscard_in;
uint32_t vscard_in_hdr;
uint8_t atr[MAX_ATR_SIZE];
uint8_t atr_length;
uint8_t debug;
+ uint32_t outbuffer_max;
+ Buffer vscard_out;
+ QEMUTimer *vscard_out_timer;
};
/*
+ * return 0 on success
+ * return 1 if not enough place for data (none is copied)
+ */
+static int buffer_append(PassthruState *card, Buffer *buffer,
+ const uint8_t *data, uint32_t length)
+{
+ if (buffer->pos + length > buffer->size) {
+ if (buffer->resize) {
+ if (buffer->max_size > buffer->pos + length) {
+ return 1;
+ }
+ DPRINTF(card, D_VERBOSE, "%s: realloc to %d\n", __func__,
+ buffer->pos + length);
+ buffer->size = buffer->pos + length;
+ buffer->data = qemu_oom_check(realloc(buffer->data, buffer->size));
+ } else {
+ return 1;
+ }
+ }
+ assert(buffer->pos < buffer->size);
+ memcpy(buffer->data + buffer->pos, data, length);
+ buffer->pos += length;
+ return 0;
+}
+
+static void ccid_card_cs_write(PassthruState *s,
+ const uint8_t *buffer, uint32_t length)
+{
+ int written = qemu_chr_fe_write(s->cs, buffer, length);
+ int remaining = length - written;
+
+ if (remaining == 0) {
+ return;
+ }
+ buffer_append(s, &s->vscard_out, buffer, length);
+}
+
+/*
* VSCard protocol over chardev
* This code should not depend on the card type.
*/
@@ -72,8 +123,8 @@ static void ccid_card_vscard_send_msg(PassthruState *s,
scr_msg_header.type = htonl(type);
scr_msg_header.reader_id = htonl(reader_id);
scr_msg_header.length = htonl(length);
- qemu_chr_fe_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader));
- qemu_chr_fe_write(s->cs, payload, length);
+ ccid_card_cs_write(s, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader));
+ ccid_card_cs_write(s, payload, length);
}
static void ccid_card_vscard_send_apdu(PassthruState *s,
@@ -108,8 +159,8 @@ static int ccid_card_vscard_can_read(void *opaque)
{
PassthruState *card = opaque;
- return VSCARD_IN_SIZE >= card->vscard_in_pos ?
- VSCARD_IN_SIZE - card->vscard_in_pos : 0;
+ return card->vscard_in.size >= card->vscard_in.pos ?
+ card->vscard_in.size - card->vscard_in.pos : 0;
}
static void ccid_card_vscard_handle_init(
@@ -200,7 +251,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
static void ccid_card_vscard_drop_connection(PassthruState *card)
{
qemu_chr_delete(card->cs);
- card->vscard_in_pos = card->vscard_in_hdr = 0;
+ card->vscard_in.pos = card->vscard_in_hdr = 0;
}
static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
@@ -208,31 +259,28 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
PassthruState *card = opaque;
VSCMsgHeader *hdr;
- if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
+ if (buffer_append(card, &card->vscard_in, buf, size)) {
error_report(
- "no room for data: pos %d + size %d > %d. dropping connection.",
- card->vscard_in_pos, size, VSCARD_IN_SIZE);
+ "no room for data: pos %d + size %d > %d. dropping connection.",
+ card->vscard_in.pos, size, card->vscard_in.size);
ccid_card_vscard_drop_connection(card);
return;
}
- assert(card->vscard_in_pos < VSCARD_IN_SIZE);
assert(card->vscard_in_hdr < VSCARD_IN_SIZE);
- memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
- card->vscard_in_pos += size;
- hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
+ hdr = (VSCMsgHeader *)(card->vscard_in.data + card->vscard_in_hdr);
- while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
- &&(card->vscard_in_pos - card->vscard_in_hdr >=
+ while ((card->vscard_in.pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
+ && (card->vscard_in.pos - card->vscard_in_hdr >=
sizeof(VSCMsgHeader) + ntohl(hdr->length))) {
hdr->reader_id = ntohl(hdr->reader_id);
hdr->length = ntohl(hdr->length);
hdr->type = ntohl(hdr->type);
ccid_card_vscard_handle_message(card, hdr);
card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
- hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
+ hdr = (VSCMsgHeader *)(card->vscard_in.data + card->vscard_in_hdr);
}
- if (card->vscard_in_hdr == card->vscard_in_pos) {
- card->vscard_in_pos = card->vscard_in_hdr = 0;
+ if (card->vscard_in_hdr == card->vscard_in.pos) {
+ card->vscard_in.pos = card->vscard_in_hdr = 0;
}
}
@@ -242,7 +290,8 @@ static void ccid_card_vscard_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_BREAK:
- card->vscard_in_pos = card->vscard_in_hdr = 0;
+ DPRINTF(card, D_INFO, "%s: CHR_EVENT_BREAK\n", __func__);
+ card->vscard_in.pos = card->vscard_in_hdr = 0;
break;
case CHR_EVENT_FOCUS:
break;
@@ -274,11 +323,50 @@ static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
return card->atr;
}
+static void buffer_init(Buffer *buf, uint32_t max_size, int resizable,
+ int alloc_up_front)
+{
+ buf->max_size = max_size;
+ buf->resize = resizable;
+ buf->pos = 0;
+ if (alloc_up_front) {
+ buf->size = max_size;
+ } else {
+ buf->size = 1024;
+ }
+ buf->data = qemu_oom_check(malloc(buf->size));
+}
+
+static void buffer_write_out(Buffer *buf, CharDriverState *cs)
+{
+ int written;
+
+ written = qemu_chr_fe_write(cs,
+ buf->data + buf->pos,
+ buf->size - buf->pos);
+ buf->pos += written;
+ if (buf->pos == buf->size) {
+ buf->size = buf->pos = 0;
+ }
+}
+
+static void ccid_card_passthru_vscard_out_timeout(void *opaque)
+{
+ PassthruState *card = opaque;
+
+ buffer_write_out(&card->vscard_out, card->cs);
+ if (card->vscard_out.size > 0) {
+ qemu_mod_timer(card->vscard_out_timer,
+ qemu_get_clock_ms(vm_clock) +
+ VSCARD_OUT_TIMER_DELTA_MS);
+ }
+}
+
static int passthru_initfn(CCIDCardState *base)
{
PassthruState *card = DO_UPCAST(PassthruState, base, base);
- card->vscard_in_pos = 0;
+ buffer_init(&card->vscard_in, VSCARD_IN_SIZE, 0, 1);
card->vscard_in_hdr = 0;
if (card->cs) {
DPRINTF(card, D_INFO, "initing chardev\n");
@@ -294,6 +382,9 @@ static int passthru_initfn(CCIDCardState *base)
assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
card->atr_length = sizeof(DEFAULT_ATR);
+ buffer_init(&card->vscard_out, card->outbuffer_max, 0, 0);
+ card->vscard_out_timer = qemu_new_timer_ms(vm_clock,
+ ccid_card_passthru_vscard_out_timeout, card);
return 0;
}
@@ -302,16 +393,30 @@ static int passthru_exitfn(CCIDCardState *base)
return 0;
}
+static VMStateDescription buffer_vmstate = {
+ .name = PASSTHRU_DEV_NAME "/buffer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pos, Buffer),
+ VMSTATE_UINT32(size, Buffer),
+ VMSTATE_UINT32(max_size, Buffer),
+ VMSTATE_UINT32(resize, Buffer),
+ VMSTATE_VBUFFER_UINT32(data, Buffer, 1, NULL, 0, size),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static VMStateDescription passthru_vmstate = {
.name = PASSTHRU_DEV_NAME,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
- VMSTATE_BUFFER(vscard_in_data, PassthruState),
- VMSTATE_UINT32(vscard_in_pos, PassthruState),
+ VMSTATE_STRUCT(vscard_in, PassthruState, 1, buffer_vmstate, Buffer),
VMSTATE_UINT32(vscard_in_hdr, PassthruState),
VMSTATE_BUFFER(atr, PassthruState),
VMSTATE_UINT8(atr_length, PassthruState),
+ VMSTATE_STRUCT(vscard_out, PassthruState, 1, buffer_vmstate, Buffer),
VMSTATE_END_OF_LIST()
}
};
@@ -319,6 +424,7 @@ static VMStateDescription passthru_vmstate = {
static Property passthru_card_properties[] = {
DEFINE_PROP_CHR("chardev", PassthruState, cs),
DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
+ DEFINE_PROP_UINT32("outbuffer", PassthruState, outbuffer_max, 1024*1024),
DEFINE_PROP_END_OF_LIST(),
};