From fa57ee8ed246cfea53acd09663203deda64b4f33 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 4 Aug 2011 14:54:19 +0200 Subject: [PATCH 01/16] re-activate usb-host for bsd A bunch of code was disabled via #if 0, for a quite long time (since Sept 2009). Surprisingly the code builds just fine when they are removed (tested on OpenBSD). /me wonders nevertheless whenever there are any users of those bits when this went unnoticed for almost two years ... Signed-off-by: Gerd Hoffmann --- usb-bsd.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/usb-bsd.c b/usb-bsd.c index 3b97eb491b..ab8e3b7294 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -62,7 +62,6 @@ typedef struct USBHostDevice { } USBHostDevice; -#if 0 static int ensure_ep_open(USBHostDevice *dev, int ep, int mode) { char buf[32]; @@ -110,7 +109,6 @@ static void ensure_eps_closed(USBHostDevice *dev) epnum++; } } -#endif static void usb_host_handle_reset(USBDevice *dev) { @@ -119,7 +117,6 @@ static void usb_host_handle_reset(USBDevice *dev) #endif } -#if 0 /* XXX: * -check device states against transfer requests * and return appropriate response @@ -278,7 +275,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return ret; } } -#endif static void usb_host_handle_destroy(USBDevice *opaque) { @@ -305,8 +301,8 @@ static int usb_host_initfn(USBDevice *dev) USBDevice *usb_host_device_open(const char *devname) { struct usb_device_info bus_info, dev_info; - USBDevice *d = NULL; - USBHostDevice *dev, *ret = NULL; + USBDevice *d = NULL, *ret = NULL; + USBHostDevice *dev; char ctlpath[PATH_MAX + 1]; char buspath[PATH_MAX + 1]; int bfd, dfd, bus, address, i; @@ -408,10 +404,8 @@ static struct USBDeviceInfo usb_host_dev_info = { .init = usb_host_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_host_handle_reset, -#if 0 .handle_control = usb_host_handle_control, .handle_data = usb_host_handle_data, -#endif .handle_destroy = usb_host_handle_destroy, }; From 3a1dca94d6dba00fe0fd4c4a28449f57e01b9b6c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 13:35:10 +0200 Subject: [PATCH 02/16] Add iov_hexdump() Useful for debugging purposes. Signed-off-by: Gerd Hoffmann --- iov.c | 31 +++++++++++++++++++++++++++++++ iov.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/iov.c b/iov.c index 1e027914d4..60553c7542 100644 --- a/iov.c +++ b/iov.c @@ -73,3 +73,34 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) } return len; } + +void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit) +{ + unsigned int i, v, b; + uint8_t *c; + + c = iov[0].iov_base; + for (i = 0, v = 0, b = 0; b < limit; i++, b++) { + if (i == iov[v].iov_len) { + i = 0; v++; + if (v == iov_cnt) { + break; + } + c = iov[v].iov_base; + } + if ((b % 16) == 0) { + fprintf(fp, "%s: %04x:", prefix, b); + } + if ((b % 4) == 0) { + fprintf(fp, " "); + } + fprintf(fp, " %02x", c[i]); + if ((b % 16) == 15) { + fprintf(fp, "\n"); + } + } + if ((b % 16) != 0) { + fprintf(fp, "\n"); + } +} diff --git a/iov.h b/iov.h index 110f67ab53..c2c5b39b8d 100644 --- a/iov.h +++ b/iov.h @@ -17,3 +17,5 @@ size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, void *buf, size_t iov_off, size_t size); size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); +void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit); From 8d15028ec03999fa6eca8dba7ef7cd4eb575486b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 15:16:08 +0200 Subject: [PATCH 03/16] Add iov_clear() Fill the spefified area with zeros. Signed-off-by: Gerd Hoffmann --- iov.c | 23 +++++++++++++++++++++++ iov.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/iov.c b/iov.c index 60553c7542..e7385c41f4 100644 --- a/iov.c +++ b/iov.c @@ -62,6 +62,29 @@ size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, return buf_off; } +size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt, + size_t iov_off, size_t size) +{ + size_t iovec_off, buf_off; + unsigned int i; + + iovec_off = 0; + buf_off = 0; + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); + + memset(iov[i].iov_base + (iov_off - iovec_off), 0, len); + + buf_off += len; + iov_off += len; + size -= len; + } + iovec_off += iov[i].iov_len; + } + return buf_off; +} + size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) { size_t len; diff --git a/iov.h b/iov.h index c2c5b39b8d..94d2f78284 100644 --- a/iov.h +++ b/iov.h @@ -17,5 +17,7 @@ size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, void *buf, size_t iov_off, size_t size); size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); +size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt, + size_t iov_off, size_t size); void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, FILE *fp, const char *prefix, size_t limit); From d35bf9ade5293171f13bc5fd1460920a258e3e39 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 13:36:23 +0200 Subject: [PATCH 04/16] move QEMUSGList typedef Move the QEMUSGList typedef to qemu-common so it can easily be used. The actual struct definition stays in dma.h. Signed-off-by: Gerd Hoffmann --- dma.h | 4 ++-- qemu-common.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dma.h b/dma.h index 3d8324bb54..a6db5bacbb 100644 --- a/dma.h +++ b/dma.h @@ -20,12 +20,12 @@ typedef struct { target_phys_addr_t len; } ScatterGatherEntry; -typedef struct { +struct QEMUSGList { ScatterGatherEntry *sg; int nsg; int nalloc; target_phys_addr_t size; -} QEMUSGList; +}; void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint); void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base, diff --git a/qemu-common.h b/qemu-common.h index 1e3c66511e..c7064d3620 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -270,6 +270,7 @@ typedef struct I2SCodec I2SCodec; typedef struct SSIBus SSIBus; typedef struct EventNotifier EventNotifier; typedef struct VirtIODevice VirtIODevice; +typedef struct QEMUSGList QEMUSGList; typedef uint64_t pcibus_t; From 4f4321c11ff6e98583846bfd6f0e81954924b003 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 15:22:25 +0200 Subject: [PATCH 05/16] usb: use iovecs in USBPacket Zap data pointer from USBPacket, add a QEMUIOVector instead. Add a bunch of helper functions to manage USBPacket data. Switch over users to the new interface. Note that USBPacket->len was used for two purposes: First to pass in the buffer size and second to return the number of transfered bytes or the status code on async transfers. There is a new result variable for the latter. A new status code was added to catch uninitialized result. Nobody creates iovecs with more than one element (yet). Some users are (temporarely) limited to iovecs with a single element to keep the patch size as small as possible. Signed-off-by: Gerd Hoffmann --- Makefile.objs | 1 + hw/bt-hid.c | 16 ++++---- hw/milkymist-softusb.c | 8 ++-- hw/usb-bt.c | 31 ++++++--------- hw/usb-ccid.c | 46 +++++++++++----------- hw/usb-ehci.c | 21 ++++------- hw/usb-hid.c | 6 ++- hw/usb-hub.c | 8 ++-- hw/usb-libhw.c | 63 +++++++++++++++++++++++++++++++ hw/usb-msd.c | 12 ++++-- hw/usb-musb.c | 22 +++++------ hw/usb-net.c | 65 +++++++++++-------------------- hw/usb-ohci.c | 23 ++++++----- hw/usb-serial.c | 5 ++- hw/usb-uhci.c | 38 +++++++++---------- hw/usb-wacom.c | 6 ++- hw/usb.c | 86 +++++++++++++++++++++++++++++++++++------- hw/usb.h | 13 ++++++- usb-bsd.c | 4 +- usb-linux.c | 27 ++++++------- usb-redir.c | 59 ++++++++++++++++------------- 21 files changed, 338 insertions(+), 222 deletions(-) create mode 100644 hw/usb-libhw.c diff --git a/Makefile.objs b/Makefile.objs index 6991a9f52a..3d1a4de33c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -172,6 +172,7 @@ user-obj-y += cutils.o cache-utils.o hw-obj-y = hw-obj-y += vl.o loader.o hw-obj-$(CONFIG_VIRTIO) += virtio-console.o +hw-obj-y += usb-libhw.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-y += fw_cfg.o hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o diff --git a/hw/bt-hid.c b/hw/bt-hid.c index 09120af074..a4204f98e7 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -127,11 +127,11 @@ static int bt_hid_out(struct bt_hid_device_s *s) USBPacket p; if (s->data_type == BT_DATA_OUTPUT) { - p.pid = USB_TOKEN_OUT; - p.devep = 1; - p.data = s->dataout.buffer; - p.len = s->dataout.len; + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_OUT, 0, 1); + usb_packet_addbuf(&p, s->dataout.buffer, s->dataout.len); s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); return s->dataout.len; } @@ -150,11 +150,11 @@ static int bt_hid_in(struct bt_hid_device_s *s) { USBPacket p; - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->datain.buffer; - p.len = sizeof(s->datain.buffer); + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); + usb_packet_addbuf(&p, s->dataout.buffer, sizeof(s->datain.buffer)); s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); return s->datain.len; } diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index abf7b59acd..75c85aeb6f 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque) USBPacket p; - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->kbd_usb_buffer; - p.len = sizeof(s->kbd_usb_buffer); + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); + usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer)); s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); softusb_kbd_changed(s); } diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 4557802bbc..529fa3355d 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, if (likely(!fifo->len)) return USB_RET_STALL; - len = MIN(p->len, fifo->fifo[fifo->start].len); - memcpy(p->data, fifo->fifo[fifo->start].data, len); - if (len == p->len) { + len = MIN(p->iov.size, fifo->fifo[fifo->start].len); + usb_packet_copy(p, fifo->fifo[fifo->start].data, len); + if (len == p->iov.size) { fifo->fifo[fifo->start].len -= len; fifo->fifo[fifo->start].data += len; } else { @@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, struct usb_hci_out_fifo_s *fifo, void (*send)(struct HCIInfo *, const uint8_t *, int), int (*complete)(const uint8_t *, int), - const uint8_t *data, int len) + USBPacket *p) { - if (fifo->len) { - memcpy(fifo->data + fifo->len, data, len); - fifo->len += len; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - } else if (complete(data, len)) - send(s->hci, data, len); - else { - memcpy(fifo->data, data, len); - fifo->len = len; + usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); + fifo->len += p->iov.size; + if (complete(fifo->data, fifo->len)) { + send(s->hci, fifo->data, fifo->len); + fifo->len = 0; } /* TODO: do we need to loop? */ @@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): if (s->config) usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, data, length); + usb_bt_hci_cmd_complete, p); break; default: fail: @@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) switch (p->devep & 0xf) { case USB_ACL_EP: usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p->data, p->len); + usb_bt_hci_acl_complete, p); break; case USB_SCO_EP: usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p->data, p->len); + usb_bt_hci_sco_complete, p); break; default: diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 4dda2c4833..66aeb211af 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) { CCID_Header *ccid_header; - if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { + if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { return USB_RET_STALL; } ccid_header = (CCID_Header *)s->bulk_out_data; - memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len); - s->bulk_out_pos += p->len; - if (p->len == CCID_MAX_PACKET_SIZE) { + usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); + s->bulk_out_pos += p->iov.size; + if (p->iov.size == CCID_MAX_PACKET_SIZE) { DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%d/%d)\n", - p->len, ccid_header->dwLength); + "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", + p->iov.size, ccid_header->dwLength); return 0; } if (s->bulk_out_pos < 10) { @@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) return 0; } -static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) +static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) { int ret = 0; - assert(len > 0); + assert(p->iov.size > 0); ccid_bulk_in_get(s); if (s->current_bulk_in != NULL) { - ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len); - memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret); + ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, + p->iov.size); + usb_packet_copy(p, s->current_bulk_in->data + + s->current_bulk_in->pos, ret); s->current_bulk_in->pos += ret; if (s->current_bulk_in->pos == s->current_bulk_in->len) { ccid_bulk_in_release(s); @@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) } if (ret > 0) { DPRINTF(s, D_MORE_INFO, - "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret); + "%s: %zd/%d req/act to guest (BULK_IN)\n", + __func__, p->iov.size, ret); } - if (ret != USB_RET_NAK && ret < len) { + if (ret != USB_RET_NAK && ret < p->iov.size) { DPRINTF(s, 1, - "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len); + "%s: returning short (EREMOTEIO) %d < %zd\n", + __func__, ret, p->iov.size); } return ret; } @@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) { USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); int ret = 0; - uint8_t *data = p->data; - int len = p->len; + uint8_t buf[2]; switch (p->pid) { case USB_TOKEN_OUT: @@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: switch (p->devep & 0xf) { case CCID_BULK_IN_EP: - if (!len) { + if (!p->iov.size) { ret = USB_RET_NAK; } else { - ret = ccid_bulk_in_copy_to_guest(s, data, len); + ret = ccid_bulk_in_copy_to_guest(s, p); } break; case CCID_INT_IN_EP: if (s->notify_slot_change) { /* page 56, RDR_to_PC_NotifySlotChange */ - data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; - data[1] = s->bmSlotICCState; + buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; + buf[1] = s->bmSlotICCState; + usb_packet_copy(p, buf, 2); ret = 2; s->notify_slot_change = false; s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; DPRINTF(s, D_INFO, "handle_data: int_in: notify_slot_change %X, " - "requested len %d\n", - s->bmSlotICCState, len); + "requested len %zd\n", + s->bmSlotICCState, p->iov.size); } break; default: diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 8b0dcc335d..799e31a319 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1235,7 +1235,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) trace_usb_ehci_queue_action(q, "wakeup"); assert(q->async == EHCI_ASYNC_INFLIGHT); q->async = EHCI_ASYNC_FINISHED; - q->usb_status = packet->len; + q->usb_status = packet->result; } static void ehci_execute_complete(EHCIQueue *q) @@ -1367,17 +1367,15 @@ static int ehci_execute(EHCIQueue *q) continue; } - q->packet.pid = q->pid; - q->packet.devaddr = devadr; - q->packet.devep = endp; - q->packet.data = q->buffer; - q->packet.len = q->tbytes; + usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_addbuf(&q->packet, q->buffer, q->tbytes); ret = usb_handle_packet(dev, &q->packet); - DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", + DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " + "(total %d) endp %x ret %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.len, q->tbytes, endp, ret); + q->packet.iov.size, q->tbytes, endp, ret); if (ret != USB_RET_NODEV) { break; @@ -1457,11 +1455,8 @@ static int ehci_process_itd(EHCIState *ehci, continue; } - ehci->ipacket.pid = pid; - ehci->ipacket.devaddr = devaddr; - ehci->ipacket.devep = endp; - ehci->ipacket.data = ehci->ibuffer; - ehci->ipacket.len = len; + usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + usb_packet_addbuf(&ehci->ipacket, ehci->ibuffer, len); ret = usb_handle_packet(dev, &ehci->ipacket); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 9008320c86..541644a06d 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -816,6 +816,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) { USBHIDState *s = (USBHIDState *)dev; + uint8_t buf[p->iov.size]; int ret = 0; switch(p->pid) { @@ -826,11 +827,12 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return USB_RET_NAK; usb_hid_set_next_idle(s, curtime); if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, p->data, p->len); + ret = usb_pointer_poll(s, buf, p->iov.size); } else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, p->data, p->len); + ret = usb_keyboard_poll(s, buf, p->iov.size); } + usb_packet_copy(p, buf, ret); s->changed = s->n > 0; } else { goto fail; diff --git a/hw/usb-hub.c b/hw/usb-hub.c index b49a2fe882..c49c547d0c 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -394,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) if (p->devep == 1) { USBHubPort *port; unsigned int status; + uint8_t buf[4]; int i, n; n = (NUM_PORTS + 1 + 7) / 8; - if (p->len == 1) { /* FreeBSD workaround */ + if (p->iov.size == 1) { /* FreeBSD workaround */ n = 1; - } else if (n > p->len) { + } else if (n > p->iov.size) { return USB_RET_BABBLE; } status = 0; @@ -409,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) } if (status != 0) { for(i = 0; i < n; i++) { - p->data[i] = status >> (8 * i); + buf[i] = status >> (8 * i); } + usb_packet_copy(p, buf, n); ret = n; } else { ret = USB_RET_NAK; /* usb11 11.13.1 */ diff --git a/hw/usb-libhw.c b/hw/usb-libhw.c new file mode 100644 index 0000000000..162b42bd5b --- /dev/null +++ b/hw/usb-libhw.c @@ -0,0 +1,63 @@ +/* + * QEMU USB emulation, libhw bits. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "cpu-common.h" +#include "usb.h" +#include "dma.h" + +int usb_packet_map(USBPacket *p, QEMUSGList *sgl) +{ + int is_write = (p->pid == USB_TOKEN_IN); + target_phys_addr_t len; + void *mem; + int i; + + for (i = 0; i < sgl->nsg; i++) { + len = sgl->sg[i].len; + mem = cpu_physical_memory_map(sgl->sg[i].base, &len, + is_write); + if (!mem) { + goto err; + } + qemu_iovec_add(&p->iov, mem, len); + if (len != sgl->sg[i].len) { + goto err; + } + } + return 0; + +err: + usb_packet_unmap(p); + return -1; +} + +void usb_packet_unmap(USBPacket *p) +{ + int is_write = (p->pid == USB_TOKEN_IN); + int i; + + for (i = 0; i < p->iov.niov; i++) { + cpu_physical_memory_unmap(p->iov.iov[i].iov_base, + p->iov.iov[i].iov_len, is_write, + p->iov.iov[i].iov_len); + } +} diff --git a/hw/usb-msd.c b/hw/usb-msd.c index cdeac581e3..48e0b343ca 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -207,8 +207,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) csw.residue = s->residue; csw.status = s->result; - len = MIN(sizeof(csw), p->len); - memcpy(p->data, &csw, len); + len = MIN(sizeof(csw), p->iov.size); + usb_packet_copy(p, &csw, len); + p->result = len; } static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) @@ -222,6 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) if (p) { usb_msd_copy_data(s); if (s->packet && s->usb_len == 0) { + p->result = p->iov.size; /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ @@ -257,6 +259,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } + p->result = p->iov.size; } s->packet = NULL; usb_packet_complete(&s->dev, p); @@ -342,9 +345,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) int ret = 0; struct usb_msd_cbw cbw; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; + uint8_t *data = p->iov.iov[0].iov_base; + int len = p->iov.iov[0].iov_len; + assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 035dda8372..d3ccde9199 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -365,6 +365,8 @@ struct MUSBState *musb_init(qemu_irq *irqs) s->ep[i].maxp[1] = 0x40; s->ep[i].musb = s; s->ep[i].epnum = i; + usb_packet_init(&s->ep[i].packey[0].p); + usb_packet_init(&s->ep[i].packey[1].p); } usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); @@ -605,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].p.devaddr = ep->faddr[idx]; - ep->packey[dir].p.devep = ep->type[idx] & 0xf; - ep->packey[dir].p.data = (void *) ep->buf[idx]; - ep->packey[dir].p.len = len; + usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx], + ep->type[idx] & 0xf); + usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; @@ -738,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) if (ep->status[1] == USB_RET_STALL) { ep->status[1] = 0; - packey->len = 0; + packey->result = 0; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; if (!epnum) @@ -752,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) * Data-errors in Isochronous. */ if (ep->interrupt[1]) return musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->len, musb_rx_packet_complete, 1); + packey->iov.size, musb_rx_packet_complete, 1); ep->csr[1] |= MGC_M_RXCSR_DATAERROR; if (!epnum) @@ -777,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) /* TODO: check len for over/underruns of an OUT packet? */ /* TODO: perhaps make use of e->ext_size[1] here. */ - packey->len = ep->status[1]; + packey->result = ep->status[1]; if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; if (!epnum) ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */ + ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */ /* In DMA mode: assert DMA request for this EP */ } @@ -856,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum) * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.len) { + ep->packey[1].p.iov.size) { TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), + ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; diff --git a/hw/usb-net.c b/hw/usb-net.c index 4212e5b3c5..0cb47d63b3 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -29,6 +29,7 @@ #include "net.h" #include "qemu-queue.h" #include "sysemu.h" +#include "iov.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) { + le32 buf[2]; int ret = 8; - if (p->len < 8) + if (p->iov.size < 8) { return USB_RET_STALL; + } - ((le32 *) p->data)[0] = cpu_to_le32(1); - ((le32 *) p->data)[1] = cpu_to_le32(0); + buf[0] = cpu_to_le32(1); + buf[1] = cpu_to_le32(0); + usb_packet_copy(p, buf, 8); if (!s->rndis_resp.tqh_first) ret = USB_RET_NAK; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: interrupt poll len %zu return %d", + p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) return ret; } ret = s->in_len - s->in_ptr; - if (ret > p->len) - ret = p->len; - memcpy(p->data, &s->in_buf[s->in_ptr], ret); + if (ret > p->iov.size) { + ret = p->iov.size; + } + usb_packet_copy(p, &s->in_buf[s->in_ptr], ret); s->in_ptr += ret; if (s->in_ptr >= s->in_len && (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { @@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) } #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) { - int ret = p->len; + int ret = p->iov.size; int sz = sizeof(s->out_buf) - s->out_ptr; struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *) s->out_buf; uint32_t len; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data out len %u\n", p->len); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < p->len; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); #endif if (sz > ret) sz = ret; - memcpy(&s->out_buf[s->out_ptr], p->data, sz); + usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); s->out_ptr += sz; if (!is_rndis(s)) { @@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) } if (ret == USB_RET_STALL) fprintf(stderr, "usbnet: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%x\n", - p->pid, p->devep, p->len); + "pid 0x%x ep 0x%x len 0x%zx\n", + p->pid, p->devep, p->iov.size); return ret; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 337b250261..d39bcb0c0d 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -777,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; } else { ret = USB_RET_NODEV; for (i = 0; i < ohci->num_ports; i++) { dev = ohci->rhport[i].port.dev; if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) continue; - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -959,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } #endif if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; ohci->async_td = 0; ohci->async_complete = 0; } else { @@ -980,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) #endif return 1; } - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -1761,6 +1759,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, ohci->localmem_base = localmem_base; ohci->name = dev->info->name; + usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; qemu_register_reset(ohci_reset, ohci); diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 298c1e9d95..09731da485 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -361,10 +361,11 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) USBSerialState *s = (USBSerialState *)dev; int ret = 0; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; + uint8_t *data = p->iov.iov[0].iov_base; + int len = p->iov.iov[0].iov_len; int first_len; + assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index da74c57c62..20b829ba55 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -30,6 +30,7 @@ #include "pci.h" #include "qemu-timer.h" #include "usb-uhci.h" +#include "iov.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -93,17 +94,12 @@ static const char *pid2str(int pid) #endif #ifdef DEBUG_DUMP_DATA -static void dump_data(const uint8_t *data, int len) +static void dump_data(USBPacket *p, int ret) { - int i; - - printf("uhci: data: "); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); } #else -static void dump_data(const uint8_t *data, int len) {} +static void dump_data(USBPacket *p, int ret) {} #endif typedef struct UHCIState UHCIState; @@ -179,12 +175,14 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->token = 0; async->done = 0; async->isoc = 0; + usb_packet_init(&async->packet); return async; } static void uhci_async_free(UHCIState *s, UHCIAsync *async) { + usb_packet_cleanup(&async->packet); qemu_free(async); } @@ -648,10 +646,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) { int i, ret; - DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", - pid2str(p->pid), p->devaddr, p->devep, p->len); + DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", + pid2str(p->pid), p->devaddr, p->devep, p->iov.size); if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p->data, p->len); + dump_data(p, 0); ret = USB_RET_NODEV; for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { @@ -662,9 +660,9 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) ret = usb_handle_packet(dev, p); } - DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); + DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p->data, ret); + dump_data(p, ret); return ret; } @@ -684,7 +682,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - ret = async->packet.len; + ret = async->packet.result; if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -692,7 +690,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (ret < 0) goto out; - len = async->packet.len; + len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); /* The NAK bit may have been set by a previous frame, so clear it @@ -827,11 +825,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - async->packet.pid = pid; - async->packet.devaddr = (td->token >> 8) & 0x7f; - async->packet.devep = (td->token >> 15) & 0xf; - async->packet.data = async->buffer; - async->packet.len = max_len; + usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf); + usb_packet_addbuf(&async->packet, async->buffer, max_len); switch(pid) { case USB_TOKEN_OUT: @@ -859,7 +855,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in return 2; } - async->packet.len = len; + async->packet.result = len; done: len = uhci_complete_td(s, td, async, int_mask); diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index d76ee97e49..25580067f2 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) { USBWacomState *s = (USBWacomState *) dev; + uint8_t buf[p->iov.size]; int ret = 0; switch (p->pid) { @@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) return USB_RET_NAK; s->changed = 0; if (s->mode == WACOM_MODE_HID) - ret = usb_mouse_poll(s, p->data, p->len); + ret = usb_mouse_poll(s, buf, p->iov.size); else if (s->mode == WACOM_MODE_WACOM) - ret = usb_wacom_poll(s, p->data, p->len); + ret = usb_wacom_poll(s, buf, p->iov.size); + usb_packet_copy(p, buf, ret); break; } /* Fall through. */ diff --git a/hw/usb.c b/hw/usb.c index 27a983ca5c..685e775a00 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -25,6 +25,7 @@ */ #include "qemu-common.h" #include "usb.h" +#include "iov.h" void usb_attach(USBPort *port, USBDevice *dev) { @@ -72,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - if (p->len != 8) + if (p->iov.size != 8) { return USB_RET_STALL; - - memcpy(s->setup_buf, p->data, 8); + } + + usb_packet_copy(p, s->setup_buf, p->iov.size); s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; @@ -144,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(p->data, s->data_buf + s->setup_index, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -179,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(s->data_buf + s->setup_index, p->data, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -251,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) usb_packet_complete to complete their async control packets. */ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) { - if (p->len < 0) { + if (p->result < 0) { s->setup_state = SETUP_STATE_IDLE; } switch (s->setup_state) { case SETUP_STATE_SETUP: - if (p->len < s->setup_len) { - s->setup_len = p->len; + if (p->result < s->setup_len) { + s->setup_len = p->result; } s->setup_state = SETUP_STATE_DATA; - p->len = 8; + p->result = 8; break; case SETUP_STATE_ACK: s->setup_state = SETUP_STATE_IDLE; - p->len = 0; + p->result = 0; break; default: @@ -347,3 +351,57 @@ void usb_cancel_packet(USBPacket * p) p->owner->info->cancel_packet(p->owner, p); p->owner = NULL; } + + +void usb_packet_init(USBPacket *p) +{ + qemu_iovec_init(&p->iov, 1); +} + +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) +{ + p->pid = pid; + p->devaddr = addr; + p->devep = ep; + p->result = 0; + qemu_iovec_reset(&p->iov); +} + +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) +{ + qemu_iovec_add(&p->iov, ptr, len); +} + +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + switch (p->pid) { + case USB_TOKEN_SETUP: + case USB_TOKEN_OUT: + iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + case USB_TOKEN_IN: + iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + default: + fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); + abort(); + } + p->result += bytes; +} + +void usb_packet_skip(USBPacket *p, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + if (p->pid == USB_TOKEN_IN) { + iov_clear(p->iov.iov, p->iov.niov, p->result, bytes); + } + p->result += bytes; +} + +void usb_packet_cleanup(USBPacket *p) +{ + qemu_iovec_destroy(&p->iov); +} diff --git a/hw/usb.h b/hw/usb.h index ded2de29b9..84d04df2e1 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -285,12 +285,21 @@ struct USBPacket { int pid; uint8_t devaddr; uint8_t devep; - uint8_t *data; - int len; + QEMUIOVector iov; + int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBDevice *owner; }; +void usb_packet_init(USBPacket *p); +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep); +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); +int usb_packet_map(USBPacket *p, QEMUSGList *sgl); +void usb_packet_unmap(USBPacket *p); +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); +void usb_packet_skip(USBPacket *p, size_t bytes); +void usb_packet_cleanup(USBPacket *p); + int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_cancel_packet(USBPacket * p); diff --git a/usb-bsd.c b/usb-bsd.c index ab8e3b7294..ab84d93857 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -253,9 +253,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) } if (p->pid == USB_TOKEN_IN) - ret = read(fd, p->data, p->len); + ret = readv(fd, p->iov.iov, p->iov.niov); else - ret = write(fd, p->data, p->len); + ret = writev(fd, p->iov.iov, p->iov.niov); sigprocmask(SIG_SETMASK, &old_mask, NULL); diff --git a/usb-linux.c b/usb-linux.c index 53cc5fc00e..184f56f499 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -341,16 +341,16 @@ static void async_complete(void *opaque) if (p) { switch (aurb->urb.status) { case 0: - p->len += aurb->urb.actual_length; + p->result += aurb->urb.actual_length; break; case -EPIPE: set_halt(s, p->devep); - p->len = USB_RET_STALL; + p->result = USB_RET_STALL; break; default: - p->len = USB_RET_NAK; + p->result = USB_RET_NAK; break; } @@ -604,6 +604,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) { AsyncURB *aurb; int i, j, ret, max_packet_size, offset, len = 0; + uint8_t *buf; max_packet_size = get_max_packet_size(s, p->devep); if (max_packet_size == 0) @@ -628,19 +629,19 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) len = urb_status_to_usb_ret( aurb[i].urb.iso_frame_desc[j].status); /* Check the frame fits */ - } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + } else if (aurb[i].urb.iso_frame_desc[j].actual_length + > p->iov.size) { printf("husb: received iso data is larger then packet\n"); len = USB_RET_NAK; /* All good copy data over */ } else { len = aurb[i].urb.iso_frame_desc[j].actual_length; - memcpy(p->data, - aurb[i].urb.buffer + - j * aurb[i].urb.iso_frame_desc[0].length, - len); + buf = aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length; + usb_packet_copy(p, buf, len); } } else { - len = p->len; + len = p->iov.size; offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); /* Check the frame fits */ @@ -650,7 +651,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) } /* All good copy data over */ - memcpy(aurb[i].urb.buffer + offset, p->data, len); + usb_packet_copy(p, aurb[i].urb.buffer + offset, len); aurb[i].urb.iso_frame_desc[j].length = len; offset += len; set_iso_buffer_used(s, p->devep, offset); @@ -734,9 +735,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - rem = p->len; - pbuf = p->data; - p->len = 0; + assert(p->iov.niov == 1); /* temporary */ + rem = p->iov.iov[0].iov_len; + pbuf = p->iov.iov[0].iov_base; while (rem) { aurb = async_alloc(s); aurb->packet = p; diff --git a/usb-redir.c b/usb-redir.c index e2129931a0..9e5fce21ea 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -365,12 +365,12 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, } len = isop->len; - if (len > p->len) { + if (len > p->iov.size) { ERROR("received iso data is larger then packet ep %02X\n", ep); bufp_free(dev, isop, ep); return USB_RET_NAK; } - memcpy(p->data, isop->data, len); + usb_packet_copy(p, isop->data, len); bufp_free(dev, isop, ep); return len; } else { @@ -379,18 +379,20 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, if (dev->endpoint[EP2I(ep)].iso_started) { struct usb_redir_iso_packet_header iso_packet = { .endpoint = ep, - .length = p->len + .length = p->iov.size }; + uint8_t buf[p->iov.size]; /* No id, we look at the ep when receiving a status back */ + usb_packet_copy(p, buf, p->iov.size); usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, - p->data, p->len); + buf, p->iov.size); usbredirparser_do_write(dev->parser); } status = dev->endpoint[EP2I(ep)].iso_error; dev->endpoint[EP2I(ep)].iso_error = 0; - DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, - p->len); - return usbredir_handle_status(dev, status, p->len); + DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, + p->iov.size); + return usbredir_handle_status(dev, status, p->iov.size); } } @@ -413,10 +415,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_bulk_packet_header bulk_packet; - DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); + DPRINTF("bulk-out ep %02X len %zd id %u\n", ep, + p->iov.size, aurb->packet_id); bulk_packet.endpoint = ep; - bulk_packet.length = p->len; + bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; aurb->bulk_packet = bulk_packet; @@ -424,9 +427,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, &bulk_packet, NULL, 0); } else { - usbredir_log_data(dev, "bulk data out:", p->data, p->len); + uint8_t buf[p->iov.size]; + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, - &bulk_packet, p->data, p->len); + &bulk_packet, buf, p->iov.size); } usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -471,29 +476,31 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, } len = intp->len; - if (len > p->len) { + if (len > p->iov.size) { ERROR("received int data is larger then packet ep %02X\n", ep); bufp_free(dev, intp, ep); return USB_RET_NAK; } - memcpy(p->data, intp->data, len); + usb_packet_copy(p, intp->data, len); bufp_free(dev, intp, ep); return len; } else { /* Output interrupt endpoint, normal async operation */ AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_interrupt_packet_header interrupt_packet; + uint8_t buf[p->iov.size]; - DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, + DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size, aurb->packet_id); interrupt_packet.endpoint = ep; - interrupt_packet.length = p->len; + interrupt_packet.length = p->iov.size; aurb->interrupt_packet = interrupt_packet; - usbredir_log_data(dev, "interrupt data out:", p->data, p->len); + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, - &interrupt_packet, p->data, p->len); + &interrupt_packet, buf, p->iov.size); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } @@ -959,7 +966,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id, dev->dev.data_buf[0] = config_status->configuration; len = 1; } - aurb->packet->len = + aurb->packet->result = usbredir_handle_status(dev, config_status->status, len); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } @@ -987,7 +994,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, dev->dev.data_buf[0] = alt_setting_status->alt; len = 1; } - aurb->packet->len = + aurb->packet->result = usbredir_handle_status(dev, alt_setting_status->status, len); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } @@ -1070,7 +1077,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, len = USB_RET_STALL; } } - aurb->packet->len = len; + aurb->packet->result = len; usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } async_free(dev, aurb); @@ -1105,15 +1112,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len <= aurb->packet->len) { - memcpy(aurb->packet->data, data, data_len); + if (data_len <= aurb->packet->iov.size) { + usb_packet_copy(aurb->packet, data, data_len); } else { - ERROR("bulk buffer too small (%d > %d)\n", data_len, - aurb->packet->len); + ERROR("bulk buffer too small (%d > %zd)\n", data_len, + aurb->packet->iov.size); len = USB_RET_STALL; } } - aurb->packet->len = len; + aurb->packet->result = len; usb_packet_complete(&dev->dev, aurb->packet); } async_free(dev, aurb); @@ -1185,7 +1192,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, } if (aurb->packet) { - aurb->packet->len = usbredir_handle_status(dev, + aurb->packet->result = usbredir_handle_status(dev, interrupt_packet->status, len); usb_packet_complete(&dev->dev, aurb->packet); } From 9440b7e5553f1fbceb9afe81a08f4e56975dea6d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 10:53:23 +0200 Subject: [PATCH 06/16] usb-serial: iovec support Add full support for iovecs to usb-serial. Signed-off-by: Gerd Hoffmann --- hw/usb-serial.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 09731da485..bf2b775e83 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -359,38 +359,42 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; - int ret = 0; + int i, ret = 0; uint8_t devep = p->devep; - uint8_t *data = p->iov.iov[0].iov_base; - int len = p->iov.iov[0].iov_len; - int first_len; + struct iovec *iov; + uint8_t header[2]; + int first_len, len; - assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) goto fail; - qemu_chr_write(s->cs, data, len); + for (i = 0; i < p->iov.niov; i++) { + iov = p->iov.iov + i; + qemu_chr_write(s->cs, iov->iov_base, iov->iov_len); + } break; case USB_TOKEN_IN: if (devep != 1) goto fail; first_len = RECV_BUF - s->recv_ptr; + len = p->iov.size; if (len <= 2) { ret = USB_RET_NAK; break; } - *data++ = usb_get_modem_lines(s) | 1; + header[0] = usb_get_modem_lines(s) | 1; /* We do not have the uart details */ /* handle serial break */ if (s->event_trigger && s->event_trigger & FTDI_BI) { s->event_trigger &= ~FTDI_BI; - *data = FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); ret = 2; break; } else { - *data++ = 0; + header[1] = 0; } len -= 2; if (len > s->recv_used) @@ -401,9 +405,10 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) } if (first_len > len) first_len = len; - memcpy(data, s->recv_buf + s->recv_ptr, first_len); + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); if (len > first_len) - memcpy(data + first_len, s->recv_buf, len - first_len); + usb_packet_copy(p, s->recv_buf, len - first_len); s->recv_used -= len; s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; ret = len + 2; From b621bab436b1260b1c6f3841f3bb7d76236289dc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 11:28:17 +0200 Subject: [PATCH 07/16] usb-host: iovec support Add full support for iovecs to usb-host. The code can split large transfers into smaller ones already, we are using this to also split requests at iovec borders. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 184f56f499..5562187bd5 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -707,7 +707,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret, rem; + int ret, rem, prem, v; uint8_t *pbuf; uint8_t ep; @@ -735,10 +735,18 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - assert(p->iov.niov == 1); /* temporary */ - rem = p->iov.iov[0].iov_len; - pbuf = p->iov.iov[0].iov_base; + v = 0; + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + rem = p->iov.size; while (rem) { + if (prem == 0) { + v++; + assert(v < p->iov.niov); + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + assert(prem <= rem); + } aurb = async_alloc(s); aurb->packet = p; @@ -747,16 +755,17 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) urb->type = USBDEVFS_URB_TYPE_BULK; urb->usercontext = s; urb->buffer = pbuf; + urb->buffer_length = prem; - if (rem > MAX_USBFS_BUFFER_SIZE) { + if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) { urb->buffer_length = MAX_USBFS_BUFFER_SIZE; - aurb->more = 1; - } else { - urb->buffer_length = rem; - aurb->more = 0; } pbuf += urb->buffer_length; + prem -= urb->buffer_length; rem -= urb->buffer_length; + if (rem) { + aurb->more = 1; + } ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); From 29c74f762bc65d1f38da8624ee4182822db369bf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 12:32:06 +0200 Subject: [PATCH 08/16] usb-storage: iovec support Add full iovec support to usb-storage. Signed-off-by: Gerd Hoffmann --- hw/usb-msd.c | 107 +++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 48e0b343ca..90e57fbf6b 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -43,8 +43,6 @@ typedef struct { enum USBMSDMode mode; uint32_t scsi_len; uint8_t *scsi_buf; - uint32_t usb_len; - uint8_t *usb_buf; uint32_t data_len; uint32_t residue; uint32_t tag; @@ -176,20 +174,14 @@ static const USBDesc desc = { .str = desc_strings, }; -static void usb_msd_copy_data(MSDState *s) +static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; - len = s->usb_len; + len = p->iov.size - p->result; if (len > s->scsi_len) len = s->scsi_len; - if (s->mode == USB_MSDM_DATAIN) { - memcpy(s->usb_buf, s->scsi_buf, len); - } else { - memcpy(s->scsi_buf, s->usb_buf, len); - } - s->usb_len -= len; + usb_packet_copy(p, s->scsi_buf, len); s->scsi_len -= len; - s->usb_buf += len; s->scsi_buf += len; s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { @@ -221,9 +213,9 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) s->scsi_len = len; s->scsi_buf = scsi_req_get_buf(req); if (p) { - usb_msd_copy_data(s); - if (s->packet && s->usb_len == 0) { - p->result = p->iov.size; + usb_msd_copy_data(s, p); + p = s->packet; + if (p && p->result == p->iov.size) { /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ @@ -250,16 +242,13 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) s->mode = USB_MSDM_CBW; } else { if (s->data_len) { - s->data_len -= s->usb_len; - if (s->mode == USB_MSDM_DATAIN) { - memset(s->usb_buf, 0, s->usb_len); - } - s->usb_len = 0; + int len = (p->iov.size - p->result); + usb_packet_skip(p, len); + s->data_len -= len; } if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } - p->result = p->iov.size; } s->packet = NULL; usb_packet_complete(&s->dev, p); @@ -345,10 +334,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) int ret = 0; struct usb_msd_cbw cbw; uint8_t devep = p->devep; - uint8_t *data = p->iov.iov[0].iov_base; - int len = p->iov.iov[0].iov_len; - assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) @@ -356,11 +342,11 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_CBW: - if (len != 31) { + if (p->iov.size != 31) { fprintf(stderr, "usb-msd: Bad CBW size"); goto fail; } - memcpy(&cbw, data, 31); + usb_packet_copy(p, &cbw, 31); if (le32_to_cpu(cbw.sig) != 0x43425355) { fprintf(stderr, "usb-msd: Bad signature %08x\n", le32_to_cpu(cbw.sig)); @@ -391,36 +377,39 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->mode != USB_MSDM_CSW && s->residue == 0) { scsi_req_continue(s->req); } - ret = len; + ret = p->result; break; case USB_MSDM_DATAOUT: - DPRINTF("Data out %d/%d\n", len, s->data_len); - if (len > s->data_len) + DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len); + if (p->iov.size > s->data_len) { goto fail; + } - s->usb_buf = data; - s->usb_len = len; if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected write (len %d)\n", len); + DPRINTF("Unexpected write (len %zd)\n", p->iov.size); goto fail; } break; @@ -431,18 +420,20 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_DATAOUT: - if (s->data_len != 0 || len < 13) + if (s->data_len != 0 || p->iov.size < 13) { goto fail; + } /* Waiting for SCSI write to complete. */ s->packet = p; ret = USB_RET_ASYNC; break; case USB_MSDM_CSW: - DPRINTF("Command status %d tag 0x%x, len %d\n", - s->result, s->tag, len); - if (len < 13) + DPRINTF("Command status %d tag 0x%x, len %zd\n", + s->result, s->tag, p->iov.size); + if (p->iov.size < 13) { goto fail; + } usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; @@ -450,32 +441,32 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) break; case USB_MSDM_DATAIN: - DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len); - if (len > s->data_len) - len = s->data_len; - s->usb_buf = data; - s->usb_len = len; + DPRINTF("Data in %zd/%d, scsi_len %d\n", + p->iov.size, s->data_len, s->scsi_len); if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - memset(s->usb_buf, 0, s->usb_len); - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected read (len %d)\n", len); + DPRINTF("Unexpected read (len %zd)\n", p->iov.size); goto fail; } break; From df5e66eefb8fb05891c49d0be88e3ed9656993c5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 15:37:29 +0200 Subject: [PATCH 09/16] uhci: remove buffer Map guest memory and pass on a direct pointer instead of copying the bits to a indirect buffer. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 20b829ba55..824e3a5e8b 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -31,6 +31,7 @@ #include "qemu-timer.h" #include "usb-uhci.h" #include "iov.h" +#include "dma.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -111,6 +112,7 @@ typedef struct UHCIState UHCIState; */ typedef struct UHCIAsync { USBPacket packet; + QEMUSGList sgl; UHCIState *uhci; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; @@ -118,7 +120,6 @@ typedef struct UHCIAsync { int8_t valid; uint8_t isoc; uint8_t done; - uint8_t buffer[2048]; } UHCIAsync; typedef struct UHCIPort { @@ -176,6 +177,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->done = 0; async->isoc = 0; usb_packet_init(&async->packet); + qemu_sglist_init(&async->sgl, 1); return async; } @@ -183,6 +185,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) static void uhci_async_free(UHCIState *s, UHCIAsync *async) { usb_packet_cleanup(&async->packet); + qemu_sglist_destroy(&async->sgl); qemu_free(async); } @@ -706,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ goto out; } - if (len > 0) { - /* write the data back */ - cpu_physical_memory_write(td->buffer, async->buffer, len); - } - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ @@ -827,12 +825,12 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, (td->token >> 15) & 0xf); - usb_packet_addbuf(&async->packet, async->buffer, max_len); + qemu_sglist_add(&async->sgl, td->buffer, max_len); + usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - cpu_physical_memory_read(td->buffer, async->buffer, max_len); len = uhci_broadcast_packet(s, &async->packet); if (len >= 0) len = max_len; @@ -859,6 +857,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in done: len = uhci_complete_td(s, td, async, int_mask); + usb_packet_unmap(&async->packet); uhci_async_free(s, async); return len; } From 0ce668bc5284ffebd2d0b269ae141f9a696dbd01 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 17:36:46 +0200 Subject: [PATCH 10/16] ehci: iovec support, remove buffer Map guest memory and pass on a direct pointer instead of copying the bits to a indirect buffer. EHCI transfer descriptors can reference multiple (physical guest) pages so we'll actually start seeing usb packets wich carry iovec with more than one element. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 151 +++++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 87 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 799e31a319..2b43895315 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -28,6 +28,7 @@ #include "pci.h" #include "monitor.h" #include "trace.h" +#include "dma.h" #define EHCI_DEBUG 0 @@ -269,6 +270,7 @@ typedef struct EHCIqtd { uint32_t bufptr[5]; // Standard buffer pointer #define QTD_BUFPTR_MASK 0xfffff000 +#define QTD_BUFPTR_SH 12 } EHCIqtd; /* EHCI spec version 1.0 Section 3.6 @@ -357,7 +359,7 @@ struct EHCIQueue { uint32_t qtdaddr; // address QTD read from USBPacket packet; - uint8_t buffer[BUFF_SIZE]; + QEMUSGList sgl; int pid; uint32_t tbytes; enum async_state async; @@ -414,7 +416,7 @@ struct EHCIState { uint32_t p_fetch_addr; // which address to look at next USBPacket ipacket; - uint8_t ibuffer[BUFF_SIZE]; + QEMUSGList isgl; int isoch_pause; uint64_t last_run_ns; @@ -1165,60 +1167,58 @@ static int ehci_qh_do_overlay(EHCIQueue *q) return 0; } -static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) +static int ehci_init_transfer(EHCIQueue *q) { - int bufpos = 0; - int cpage, offset; - uint32_t head; - uint32_t tail; - - - if (!bytes) { - return 0; - } - - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); - return USB_RET_PROCERR; - } + uint32_t cpage, offset, bytes, plen; + target_phys_addr_t page; + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + qemu_sglist_init(&q->sgl, 5); - do { - /* start and end of this page */ - head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; - tail = head + ~QTD_BUFPTR_MASK + 1; - /* add offset into page */ - head |= offset; - - if (bytes <= (tail - head)) { - tail = head + bytes; + while (bytes > 0) { + if (cpage > 4) { + fprintf(stderr, "cpage out of range (%d)\n", cpage); + return USB_RET_PROCERR; } - trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos); - cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw); - - bufpos += (tail - head); - offset += (tail - head); - bytes -= (tail - head); - - if (bytes > 0) { - cpage++; + page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + page += offset; + plen = bytes; + if (plen > 4096 - offset) { + plen = 4096 - offset; offset = 0; + cpage++; } - } while (bytes > 0); - - /* save cpage */ - set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); - - /* save offset into cpage */ - q->qh.bufptr[0] &= QTD_BUFPTR_MASK; - q->qh.bufptr[0] |= offset; + qemu_sglist_add(&q->sgl, page, plen); + bytes -= plen; + } return 0; } +static void ehci_finish_transfer(EHCIQueue *q, int status) +{ + uint32_t cpage, offset; + + qemu_sglist_destroy(&q->sgl); + + if (status > 0) { + /* update cpage & offset */ + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + + offset += status; + cpage += offset >> QTD_BUFPTR_SH; + offset &= ~QTD_BUFPTR_MASK; + + set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); + q->qh.bufptr[0] &= QTD_BUFPTR_MASK; + q->qh.bufptr[0] |= offset; + } +} + static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { EHCIQueue *q; @@ -1295,10 +1295,6 @@ err: } if (q->tbytes && q->pid == USB_TOKEN_IN) { - if (ehci_buffer_rw(q, q->usb_status, 1) != 0) { - q->usb_status = USB_RET_PROCERR; - return; - } q->tbytes -= q->usb_status; } else { q->tbytes = 0; @@ -1307,6 +1303,8 @@ err: DPRINTF("updating tbytes to %d\n", q->tbytes); set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); } + ehci_finish_transfer(q, q->usb_status); + usb_packet_unmap(&q->packet); q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1346,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q) default: fprintf(stderr, "bad token\n"); break; } - if ((q->tbytes && q->pid != USB_TOKEN_IN) && - (ehci_buffer_rw(q, q->tbytes, 0) != 0)) { + if (ehci_init_transfer(q) != 0) { return USB_RET_PROCERR; } @@ -1356,6 +1353,9 @@ static int ehci_execute(EHCIQueue *q) ret = USB_RET_NODEV; + usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_map(&q->packet, &q->sgl); + // TO-DO: associating device with ehci port for(i = 0; i < NB_PORTS; i++) { port = &q->ehci->ports[i]; @@ -1367,9 +1367,6 @@ static int ehci_execute(EHCIQueue *q) continue; } - usb_packet_setup(&q->packet, q->pid, devadr, endp); - usb_packet_addbuf(&q->packet, q->buffer, q->tbytes); - ret = usb_handle_packet(dev, &q->packet); DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " @@ -1399,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci, USBPort *port; USBDevice *dev; int ret; - uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp; + uint32_t i, j, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); @@ -1424,29 +1421,23 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } + qemu_sglist_init(&ehci->isgl, 2); if (off + len > 4096) { /* transfer crosses page border */ - len2 = off + len - 4096; - len1 = len - len2; + uint32_t len2 = off + len - 4096; + uint32_t len1 = len - len2; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); + qemu_sglist_add(&ehci->isgl, ptr2, len2); } else { - len1 = len; - len2 = 0; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len); } - if (!dir) { - pid = USB_TOKEN_OUT; - trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0); - if (len2) { - trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0); - } - } else { - pid = USB_TOKEN_IN; - } + pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; + + usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + usb_packet_map(&ehci->ipacket, &ehci->isgl); ret = USB_RET_NODEV; - for (j = 0; j < NB_PORTS; j++) { port = &ehci->ports[j]; dev = port->dev; @@ -1455,9 +1446,6 @@ static int ehci_process_itd(EHCIState *ehci, continue; } - usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); - usb_packet_addbuf(&ehci->ipacket, ehci->ibuffer, len); - ret = usb_handle_packet(dev, &ehci->ipacket); if (ret != USB_RET_NODEV) { @@ -1465,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci, } } + usb_packet_unmap(&ehci->ipacket); + qemu_sglist_destroy(&ehci->isgl); + #if 0 /* In isoch, there is no facility to indicate a NAK so let's * instead just complete a zero-byte transaction. Setting @@ -1502,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci, set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); } else { /* IN */ - if (len1 > ret) { - len1 = ret; - } - if (len2 > ret - len1) { - len2 = ret - len1; - } - if (len1) { - trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1); - } - if (len2) { - trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1); - } set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } From 0d878eec1eddcf01f07c38c6040ea91184139299 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 13:12:44 +0200 Subject: [PATCH 11/16] usb-hid: create & use HIDState First step in separating out the HID emulation code from usb-hid, so it can be reused without creating a dummy usb device like bluetooth does. This creates a HIDState struct, moves the non-usbish fields from USBHIDStruct there. Renames non-usbish structs, defines and functions from usb* to hid*. Adapts the code to that. Also cleans up a bunch of code style issues along the way. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 318 +++++++++++++++++++++++++++------------------------ 1 file changed, 169 insertions(+), 149 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 541644a06d..f5d6c613a7 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -41,42 +41,46 @@ #define USB_DT_REPORT 0x22 #define USB_DT_PHY 0x23 -#define USB_MOUSE 1 -#define USB_TABLET 2 -#define USB_KEYBOARD 3 +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 -typedef struct USBPointerEvent { +typedef struct HIDPointerEvent { int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ int32_t dz, buttons_state; -} USBPointerEvent; +} HIDPointerEvent; #define QUEUE_LENGTH 16 /* should be enough for a triple-click */ #define QUEUE_MASK (QUEUE_LENGTH-1u) #define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) -typedef struct USBMouseState { - USBPointerEvent queue[QUEUE_LENGTH]; +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; QEMUPutMouseEntry *eh_entry; -} USBMouseState; +} HIDMouseState; -typedef struct USBKeyboardState { +typedef struct HIDKeyboardState { uint32_t keycodes[QUEUE_LENGTH]; uint16_t modifiers; uint8_t leds; uint8_t key[16]; int32_t keys; -} USBKeyboardState; +} HIDKeyboardState; -typedef struct USBHIDState { - USBDevice dev; +typedef struct HIDState { union { - USBMouseState ptr; - USBKeyboardState kbd; + HIDMouseState ptr; + HIDKeyboardState kbd; }; uint32_t head; /* index into circular queue */ uint32_t n; int kind; +} HIDState; + +typedef struct USBHIDState { + USBDevice dev; + HIDState hid; int32_t protocol; uint8_t idle; int64_t next_idle_clock; @@ -446,12 +450,13 @@ static void usb_hid_changed(USBHIDState *hs) usb_wakeup(&hs->dev); } -static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) { +static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) +{ e->xdx = e->ydy = e->dz = 0; e->buttons_state = buttons; } -static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, +static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, int x1, int y1, int z1) { if (xyrel) { e->xdx += x1; @@ -471,8 +476,8 @@ static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, static void usb_pointer_event(void *opaque, int x1, int y1, int z1, int buttons_state) { - USBHIDState *hs = opaque; - USBMouseState *s = &hs->ptr; + USBHIDState *us = opaque; + HIDState *hs = &us->hid; unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; @@ -483,25 +488,26 @@ static void usb_pointer_event(void *opaque, * the first event changed the button state. */ if (hs->n == QUEUE_LENGTH) { /* Queue full. Discard old button state, combine motion normally. */ - s->queue[use_slot].buttons_state = buttons_state; + hs->ptr.queue[use_slot].buttons_state = buttons_state; } else if (hs->n < 2 || - s->queue[use_slot].buttons_state != buttons_state || - s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) { + hs->ptr.queue[use_slot].buttons_state != buttons_state || + hs->ptr.queue[previous_slot].buttons_state != + hs->ptr.queue[use_slot].buttons_state) { /* Cannot or should not combine, so add an empty item to the queue. */ QUEUE_INCR(use_slot); hs->n++; - usb_pointer_event_clear(&s->queue[use_slot], buttons_state); + hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); } - usb_pointer_event_combine(&s->queue[use_slot], - hs->kind == USB_MOUSE, + hid_pointer_event_combine(&hs->ptr.queue[use_slot], + hs->kind == HID_MOUSE, x1, y1, z1); - usb_hid_changed(hs); + usb_hid_changed(us); } static void usb_keyboard_event(void *opaque, int keycode) { - USBHIDState *hs = opaque; - USBKeyboardState *s = &hs->kbd; + USBHIDState *us = opaque; + HIDState *hs = &us->hid; int slot; if (hs->n == QUEUE_LENGTH) { @@ -509,13 +515,12 @@ static void usb_keyboard_event(void *opaque, int keycode) return; } slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - s->keycodes[slot] = keycode; - usb_hid_changed(hs); + hs->kbd.keycodes[slot] = keycode; + usb_hid_changed(us); } -static void usb_keyboard_process_keycode(USBHIDState *hs) +static void hid_keyboard_process_keycode(HIDState *hs) { - USBKeyboardState *s = &hs->kbd; uint8_t hid_code, key; int i, keycode, slot; @@ -523,49 +528,55 @@ static void usb_keyboard_process_keycode(USBHIDState *hs) return; } slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; - keycode = s->keycodes[slot]; + keycode = hs->kbd.keycodes[slot]; key = keycode & 0x7f; - hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; - s->modifiers &= ~(1 << 8); + hid_code = usb_hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; + hs->kbd.modifiers &= ~(1 << 8); switch (hid_code) { case 0x00: return; case 0xe0: - if (s->modifiers & (1 << 9)) { - s->modifiers ^= 3 << 8; + if (hs->kbd.modifiers & (1 << 9)) { + hs->kbd.modifiers ^= 3 << 8; return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { - s->modifiers &= ~(1 << (hid_code & 0x0f)); + hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); return; } case 0xe8 ... 0xef: - s->modifiers |= 1 << (hid_code & 0x0f); + hs->kbd.modifiers |= 1 << (hid_code & 0x0f); return; } if (keycode & (1 << 7)) { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) { - s->key[i] = s->key[-- s->keys]; - s->key[s->keys] = 0x00; + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; + hs->kbd.key[hs->kbd.keys] = 0x00; break; } - if (i < 0) - return; - } else { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) - break; + } if (i < 0) { - if (s->keys < sizeof(s->key)) - s->key[s->keys ++] = hid_code; - } else return; + } + } else { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + break; + } + } + if (i < 0) { + if (hs->kbd.keys < sizeof(hs->kbd.key)) { + hs->kbd.key[hs->kbd.keys++] = hid_code; + } + } else { + return; + } } } @@ -579,24 +590,23 @@ static inline int int_clamp(int val, int vmin, int vmax) return val; } -static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) +static int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) { int dx, dy, dz, b, l; int index; - USBMouseState *s = &hs->ptr; - USBPointerEvent *e; + HIDPointerEvent *e; - if (!s->mouse_grabbed) { - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; + if (!hs->ptr.mouse_grabbed) { + qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + hs->ptr.mouse_grabbed = 1; } /* When the buffer is empty, return the last event. Relative movements will all be zero. */ index = (hs->n ? hs->head : hs->head - 1); - e = &s->queue[index & QUEUE_MASK]; + e = &hs->ptr.queue[index & QUEUE_MASK]; - if (hs->kind == USB_MOUSE) { + if (hs->kind == HID_MOUSE) { dx = int_clamp(e->xdx, -127, 127); dy = int_clamp(e->ydy, -127, 127); e->xdx -= dx; @@ -618,7 +628,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) if (hs->n && !e->dz && - (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) { + (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { /* that deals with this event */ QUEUE_INCR(hs->head); hs->n--; @@ -628,7 +638,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) dz = 0 - dz; l = 0; switch (hs->kind) { - case USB_MOUSE: + case HID_MOUSE: if (len > l) buf[l++] = b; if (len > l) @@ -639,7 +649,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) buf[l++] = dz; break; - case USB_TABLET: + case HID_TABLET: if (len > l) buf[l++] = b; if (len > l) @@ -661,25 +671,25 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) return l; } -static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len) +static int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) { - USBKeyboardState *s = &hs->kbd; if (len < 2) return 0; - usb_keyboard_process_keycode(hs); + hid_keyboard_process_keycode(hs); - buf[0] = s->modifiers & 0xff; + buf[0] = hs->kbd.modifiers & 0xff; buf[1] = 0; - if (s->keys > 6) + if (hs->kbd.keys > 6) { memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - else - memcpy(buf + 2, s->key, MIN(8, len) - 2); + } else { + memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); + } return MIN(8, len); } -static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) +static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) { if (len > 0) { int ledstate = 0; @@ -688,13 +698,16 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) * 0x04: Scroll Lock LED * 0x08: Compose LED * 0x10: Kana LED */ - s->leds = buf[0]; - if (s->leds & 0x04) + hs->kbd.leds = buf[0]; + if (hs->kbd.leds & 0x04) { ledstate |= QEMU_SCROLL_LOCK_LED; - if (s->leds & 0x01) + } + if (hs->kbd.leds & 0x01) { ledstate |= QEMU_NUM_LOCK_LED; - if (s->leds & 0x02) + } + if (hs->kbd.leds & 0x02) { ledstate |= QEMU_CAPS_LOCK_LED; + } kbd_put_ledstate(ledstate); } return 0; @@ -702,25 +715,25 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) static void usb_mouse_handle_reset(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - memset(s->ptr.queue, 0, sizeof (s->ptr.queue)); - s->head = 0; - s->n = 0; - s->protocol = 1; + memset(us->hid.ptr.queue, 0, sizeof(us->hid.ptr.queue)); + us->hid.head = 0; + us->hid.n = 0; + us->protocol = 1; } static void usb_keyboard_handle_reset(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - qemu_add_kbd_event_handler(usb_keyboard_event, s); - memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes)); - s->head = 0; - s->n = 0; - memset(s->kbd.key, 0, sizeof (s->kbd.key)); - s->kbd.keys = 0; - s->protocol = 1; + qemu_add_kbd_event_handler(usb_keyboard_event, us); + memset(us->hid.kbd.keycodes, 0, sizeof(us->hid.kbd.keycodes)); + us->hid.head = 0; + us->hid.n = 0; + memset(us->hid.kbd.key, 0, sizeof(us->hid.kbd.key)); + us->hid.kbd.keys = 0; + us->protocol = 1; } static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) @@ -731,7 +744,8 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; int ret; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -740,7 +754,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } ret = 0; - switch(request) { + switch (request) { case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -750,17 +764,17 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, break; /* hid specific requests */ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { + switch (value >> 8) { case 0x22: - if (s->kind == USB_MOUSE) { + if (hs->kind == HID_MOUSE) { memcpy(data, qemu_mouse_hid_report_descriptor, sizeof(qemu_mouse_hid_report_descriptor)); ret = sizeof(qemu_mouse_hid_report_descriptor); - } else if (s->kind == USB_TABLET) { - memcpy(data, qemu_tablet_hid_report_descriptor, + } else if (hs->kind == HID_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, sizeof(qemu_tablet_hid_report_descriptor)); ret = sizeof(qemu_tablet_hid_report_descriptor); - } else if (s->kind == USB_KEYBOARD) { + } else if (hs->kind == HID_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, sizeof(qemu_keyboard_hid_report_descriptor)); ret = sizeof(qemu_keyboard_hid_report_descriptor); @@ -771,38 +785,41 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } break; case GET_REPORT: - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, data, length); - } else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, data, length); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, data, length); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, data, length); } - s->changed = s->n > 0; + us->changed = hs->n > 0; break; case SET_REPORT: - if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_write(&s->kbd, data, length); - else + if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_write(hs, data, length); + } else { goto fail; + } break; case GET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 1; - data[0] = s->protocol; + data[0] = us->protocol; break; case SET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 0; - s->protocol = value; + us->protocol = value; break; case GET_IDLE: ret = 1; - data[0] = s->idle; + data[0] = us->idle; break; case SET_IDLE: - s->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + us->idle = (uint8_t) (value >> 8); + usb_hid_set_next_idle(us, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -815,25 +832,27 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; uint8_t buf[p->iov.size]; int ret = 0; - switch(p->pid) { + switch (p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); - if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) + if (!us->changed && + (!us->idle || us->next_idle_clock - curtime > 0)) { return USB_RET_NAK; - usb_hid_set_next_idle(s, curtime); - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, buf, p->iov.size); } - else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, buf, p->iov.size); + usb_hid_set_next_idle(us, curtime); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, buf, p->iov.size); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, buf, p->iov.size); } usb_packet_copy(p, buf, ret); - s->changed = s->n > 0; + us->changed = hs->n > 0; } else { goto fail; } @@ -849,50 +868,51 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) static void usb_hid_handle_destroy(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - switch(s->kind) { - case USB_KEYBOARD: + switch (us->hid.kind) { + case HID_KEYBOARD: qemu_remove_kbd_event_handler(); break; default: - qemu_remove_mouse_event_handler(s->ptr.eh_entry); + qemu_remove_mouse_event_handler(us->hid.ptr.eh_entry); } } static int usb_hid_initfn(USBDevice *dev, int kind) { - USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; usb_desc_init(dev); - s->kind = kind; + hs->kind = kind; - if (s->kind == USB_MOUSE) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 0, "QEMU USB Mouse"); - } else if (s->kind == USB_TABLET) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 1, "QEMU USB Tablet"); + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, + 1, "QEMU HID Tablet"); } /* Force poll routine to be run and grab input the first time. */ - s->changed = 1; + us->changed = 1; return 0; } static int usb_tablet_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_TABLET); + return usb_hid_initfn(dev, HID_TABLET); } static int usb_mouse_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_MOUSE); + return usb_hid_initfn(dev, HID_MOUSE); } static int usb_keyboard_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_KEYBOARD); + return usb_hid_initfn(dev, HID_KEYBOARD); } void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) @@ -918,10 +938,10 @@ static const VMStateDescription vmstate_usb_ptr_queue = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField []) { - VMSTATE_INT32(xdx, USBPointerEvent), - VMSTATE_INT32(ydy, USBPointerEvent), - VMSTATE_INT32(dz, USBPointerEvent), - VMSTATE_INT32(buttons_state, USBPointerEvent), + VMSTATE_INT32(xdx, HIDPointerEvent), + VMSTATE_INT32(ydy, HIDPointerEvent), + VMSTATE_INT32(dz, HIDPointerEvent), + VMSTATE_INT32(buttons_state, HIDPointerEvent), VMSTATE_END_OF_LIST() } }; @@ -932,10 +952,10 @@ static const VMStateDescription vmstate_usb_ptr = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0, - vmstate_usb_ptr_queue, USBPointerEvent), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), + VMSTATE_STRUCT_ARRAY(hid.ptr.queue, USBHIDState, QUEUE_LENGTH, 0, + vmstate_usb_ptr_queue, HIDPointerEvent), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), VMSTATE_INT32(protocol, USBHIDState), VMSTATE_UINT8(idle, USBHIDState), VMSTATE_END_OF_LIST() @@ -949,13 +969,13 @@ static const VMStateDescription vmstate_usb_kbd = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), - VMSTATE_UINT16(kbd.modifiers, USBHIDState), - VMSTATE_UINT8(kbd.leds, USBHIDState), - VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16), - VMSTATE_INT32(kbd.keys, USBHIDState), + VMSTATE_UINT32_ARRAY(hid.kbd.keycodes, USBHIDState, QUEUE_LENGTH), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), + VMSTATE_UINT16(hid.kbd.modifiers, USBHIDState), + VMSTATE_UINT8(hid.kbd.leds, USBHIDState), + VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16), + VMSTATE_INT32(hid.kbd.keys, USBHIDState), VMSTATE_INT32(protocol, USBHIDState), VMSTATE_UINT8(idle, USBHIDState), VMSTATE_END_OF_LIST() From 8bde6805412a2808009a84f1ce5f47b88b0352d0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 14:37:15 +0200 Subject: [PATCH 12/16] usb-hid: add event callback Add callback for event notification, which allows to un-usbify more functions. Also split separate hid_* functions for reset and release. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 116 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index f5d6c613a7..870cc66272 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -54,6 +54,9 @@ typedef struct HIDPointerEvent { #define QUEUE_MASK (QUEUE_LENGTH-1u) #define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + typedef struct HIDMouseState { HIDPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; @@ -68,7 +71,7 @@ typedef struct HIDKeyboardState { int32_t keys; } HIDKeyboardState; -typedef struct HIDState { +struct HIDState { union { HIDMouseState ptr; HIDKeyboardState kbd; @@ -76,7 +79,8 @@ typedef struct HIDState { uint32_t head; /* index into circular queue */ uint32_t n; int kind; -} HIDState; + HIDEventFunc event; +}; typedef struct USBHIDState { USBDevice dev; @@ -440,14 +444,17 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static void usb_hid_changed(USBHIDState *hs) +static void usb_hid_changed(HIDState *hs) { - hs->changed = 1; + USBHIDState *us = container_of(hs, USBHIDState, hid); - if (hs->datain) - hs->datain(hs->datain_opaque); + us->changed = 1; - usb_wakeup(&hs->dev); + if (us->datain) { + us->datain(us->datain_opaque); + } + + usb_wakeup(&us->dev); } static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) @@ -473,11 +480,10 @@ static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, e->dz += z1; } -static void usb_pointer_event(void *opaque, +static void hid_pointer_event(void *opaque, int x1, int y1, int z1, int buttons_state) { - USBHIDState *us = opaque; - HIDState *hs = &us->hid; + HIDState *hs = opaque; unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; @@ -501,13 +507,12 @@ static void usb_pointer_event(void *opaque, hid_pointer_event_combine(&hs->ptr.queue[use_slot], hs->kind == HID_MOUSE, x1, y1, z1); - usb_hid_changed(us); + hs->event(hs); } -static void usb_keyboard_event(void *opaque, int keycode) +static void hid_keyboard_event(void *opaque, int keycode) { - USBHIDState *us = opaque; - HIDState *hs = &us->hid; + HIDState *hs = opaque; int slot; if (hs->n == QUEUE_LENGTH) { @@ -516,7 +521,7 @@ static void usb_keyboard_event(void *opaque, int keycode) } slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; hs->kbd.keycodes[slot] = keycode; - usb_hid_changed(us); + hs->event(hs); } static void hid_keyboard_process_keycode(HIDState *hs) @@ -713,26 +718,29 @@ static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) return 0; } -static void usb_mouse_handle_reset(USBDevice *dev) +static void hid_handle_reset(HIDState *hs) { - USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - - memset(us->hid.ptr.queue, 0, sizeof(us->hid.ptr.queue)); - us->hid.head = 0; - us->hid.n = 0; - us->protocol = 1; + switch (hs->kind) { + case HID_KEYBOARD: + qemu_add_kbd_event_handler(hid_keyboard_event, hs); + memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); + memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); + hs->kbd.keys = 0; + break; + case HID_MOUSE: + case HID_TABLET: + memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); + break; + } + hs->head = 0; + hs->n = 0; } -static void usb_keyboard_handle_reset(USBDevice *dev) +static void usb_hid_handle_reset(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - qemu_add_kbd_event_handler(usb_keyboard_event, us); - memset(us->hid.kbd.keycodes, 0, sizeof(us->hid.kbd.keycodes)); - us->hid.head = 0; - us->hid.n = 0; - memset(us->hid.kbd.key, 0, sizeof(us->hid.kbd.key)); - us->hid.kbd.keys = 0; + hid_handle_reset(&us->hid); us->protocol = 1; } @@ -866,34 +874,46 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return ret; } +static void hid_free(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_remove_kbd_event_handler(); + break; + case HID_MOUSE: + case HID_TABLET: + qemu_remove_mouse_event_handler(hs->ptr.eh_entry); + break; + } +} + static void usb_hid_handle_destroy(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - switch (us->hid.kind) { - case HID_KEYBOARD: - qemu_remove_kbd_event_handler(); - break; - default: - qemu_remove_mouse_event_handler(us->hid.ptr.eh_entry); + hid_free(&us->hid); +} + +static void hid_init(HIDState *hs, int kind, HIDEventFunc event) +{ + hs->kind = kind; + hs->event = event; + + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 1, "QEMU HID Tablet"); } } static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - HIDState *hs = &us->hid; usb_desc_init(dev); - hs->kind = kind; - - if (hs->kind == HID_MOUSE) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, - 0, "QEMU HID Mouse"); - } else if (hs->kind == HID_TABLET) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, - 1, "QEMU HID Tablet"); - } + hid_init(&us->hid, kind, usb_hid_changed); /* Force poll routine to be run and grab input the first time. */ us->changed = 1; @@ -992,7 +1012,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -1005,7 +1025,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -1018,7 +1038,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_keyboard_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, From 38931fa8cfb074a08ce65fd1982bd4a5bef9d6fb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 14:46:39 +0200 Subject: [PATCH 13/16] usb-hid: add hid_has_events() Add hid_has_events function, use it to figure whenever there are pending events instead of checking and updating USBHIDState->changed. Setting ->changed to 1 on init is removed, that should have absolutely no effect as the initial state of ->idle is 0 so we report hid state anyway until the guest configures some idle time. Also should clear ->idle on reset. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 870cc66272..b730692e0e 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -88,7 +88,6 @@ typedef struct USBHIDState { int32_t protocol; uint8_t idle; int64_t next_idle_clock; - int changed; void *datain_opaque; void (*datain)(void *); } USBHIDState; @@ -444,12 +443,15 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static bool hid_has_events(HIDState *hs) +{ + return hs->n > 0; +} + static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - us->changed = 1; - if (us->datain) { us->datain(us->datain_opaque); } @@ -742,6 +744,7 @@ static void usb_hid_handle_reset(USBDevice *dev) hid_handle_reset(&us->hid); us->protocol = 1; + us->idle = 0; } static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) @@ -798,7 +801,6 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } else if (hs->kind == HID_KEYBOARD) { ret = hid_keyboard_poll(hs, data, length); } - us->changed = hs->n > 0; break; case SET_REPORT: if (hs->kind == HID_KEYBOARD) { @@ -849,7 +851,7 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); - if (!us->changed && + if (!hid_has_events(hs) && (!us->idle || us->next_idle_clock - curtime > 0)) { return USB_RET_NAK; } @@ -860,7 +862,6 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) ret = hid_keyboard_poll(hs, buf, p->iov.size); } usb_packet_copy(p, buf, ret); - us->changed = hs->n > 0; } else { goto fail; } @@ -914,9 +915,6 @@ static int usb_hid_initfn(USBDevice *dev, int kind) usb_desc_init(dev); hid_init(&us->hid, kind, usb_hid_changed); - - /* Force poll routine to be run and grab input the first time. */ - us->changed = 1; return 0; } From dcfda673101313472524bfac8c2fe2e1d03c8214 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 15:08:01 +0200 Subject: [PATCH 14/16] usb-hid: split hid code to hw/hid.[ch] Almost pure code motion. Unstatic hid interface functions and add them to the header file. Some renames. Some code style cleanups. Signed-off-by: Gerd Hoffmann --- Makefile.objs | 1 + hw/hid.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/hid.h | 54 +++++++ hw/usb-hid.c | 397 +------------------------------------------------- 4 files changed, 452 insertions(+), 395 deletions(-) create mode 100644 hw/hid.c create mode 100644 hw/hid.h diff --git a/Makefile.objs b/Makefile.objs index 3d1a4de33c..eb5e1dcece 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -89,6 +89,7 @@ common-obj-y += i2c.o smbus.o smbus_eeprom.o common-obj-y += eeprom93xx.o common-obj-y += scsi-disk.o cdrom.o common-obj-y += scsi-generic.o scsi-bus.o +common-obj-y += hid.o common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o common-obj-$(CONFIG_SSI) += ssi.o diff --git a/hw/hid.c b/hw/hid.c new file mode 100644 index 0000000000..1893ae59f0 --- /dev/null +++ b/hw/hid.c @@ -0,0 +1,395 @@ +/* + * QEMU HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "console.h" +#include "hid.h" + +#define HID_USAGE_ERROR_ROLLOVER 0x01 +#define HID_USAGE_POSTFAIL 0x02 +#define HID_USAGE_ERROR_UNDEFINED 0x03 + +/* Indices are QEMU keycodes, values are from HID Usage Table. Indices + * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ +static const uint8_t hid_usage_keys[0x100] = { + 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, + 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, + 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, + 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, + 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, + 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, + 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, + 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, + 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, + 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, + 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, + 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, + 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +bool hid_has_events(HIDState *hs) +{ + return hs->n > 0; +} + +static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) +{ + e->xdx = e->ydy = e->dz = 0; + e->buttons_state = buttons; +} + +static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, + int x1, int y1, int z1) { + if (xyrel) { + e->xdx += x1; + e->ydy += y1; + } else { + e->xdx = x1; + e->ydy = y1; + /* Windows drivers do not like the 0/0 position and ignore such + * events. */ + if (!(x1 | y1)) { + x1 = 1; + } + } + e->dz += z1; +} + +static void hid_pointer_event(void *opaque, + int x1, int y1, int z1, int buttons_state) +{ + HIDState *hs = opaque; + unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; + unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; + + /* We combine events where feasible to keep the queue small. We shouldn't + * combine anything with the first event of a particular button state, as + * that would change the location of the button state change. When the + * queue is empty, a second event is needed because we don't know if + * the first event changed the button state. */ + if (hs->n == QUEUE_LENGTH) { + /* Queue full. Discard old button state, combine motion normally. */ + hs->ptr.queue[use_slot].buttons_state = buttons_state; + } else if (hs->n < 2 || + hs->ptr.queue[use_slot].buttons_state != buttons_state || + hs->ptr.queue[previous_slot].buttons_state != + hs->ptr.queue[use_slot].buttons_state) { + /* Cannot or should not combine, so add an empty item to the queue. */ + QUEUE_INCR(use_slot); + hs->n++; + hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); + } + hid_pointer_event_combine(&hs->ptr.queue[use_slot], + hs->kind == HID_MOUSE, + x1, y1, z1); + hs->event(hs); +} + +static void hid_keyboard_event(void *opaque, int keycode) +{ + HIDState *hs = opaque; + int slot; + + if (hs->n == QUEUE_LENGTH) { + fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + return; + } + slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; + hs->kbd.keycodes[slot] = keycode; + hs->event(hs); +} + +static void hid_keyboard_process_keycode(HIDState *hs) +{ + uint8_t hid_code, key; + int i, keycode, slot; + + if (hs->n == 0) { + return; + } + slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; + keycode = hs->kbd.keycodes[slot]; + + key = keycode & 0x7f; + hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; + hs->kbd.modifiers &= ~(1 << 8); + + switch (hid_code) { + case 0x00: + return; + + case 0xe0: + if (hs->kbd.modifiers & (1 << 9)) { + hs->kbd.modifiers ^= 3 << 8; + return; + } + case 0xe1 ... 0xe7: + if (keycode & (1 << 7)) { + hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); + return; + } + case 0xe8 ... 0xef: + hs->kbd.modifiers |= 1 << (hid_code & 0x0f); + return; + } + + if (keycode & (1 << 7)) { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; + hs->kbd.key[hs->kbd.keys] = 0x00; + break; + } + } + if (i < 0) { + return; + } + } else { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + break; + } + } + if (i < 0) { + if (hs->kbd.keys < sizeof(hs->kbd.key)) { + hs->kbd.key[hs->kbd.keys++] = hid_code; + } + } else { + return; + } + } +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) { + return vmin; + } else if (val > vmax) { + return vmax; + } else { + return val; + } +} + +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + int index; + HIDPointerEvent *e; + + if (!hs->ptr.mouse_grabbed) { + qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + hs->ptr.mouse_grabbed = 1; + } + + /* When the buffer is empty, return the last event. Relative + movements will all be zero. */ + index = (hs->n ? hs->head : hs->head - 1); + e = &hs->ptr.queue[index & QUEUE_MASK]; + + if (hs->kind == HID_MOUSE) { + dx = int_clamp(e->xdx, -127, 127); + dy = int_clamp(e->ydy, -127, 127); + e->xdx -= dx; + e->ydy -= dy; + } else { + dx = e->xdx; + dy = e->ydy; + } + dz = int_clamp(e->dz, -127, 127); + e->dz -= dz; + + b = 0; + if (e->buttons_state & MOUSE_EVENT_LBUTTON) { + b |= 0x01; + } + if (e->buttons_state & MOUSE_EVENT_RBUTTON) { + b |= 0x02; + } + if (e->buttons_state & MOUSE_EVENT_MBUTTON) { + b |= 0x04; + } + + if (hs->n && + !e->dz && + (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { + /* that deals with this event */ + QUEUE_INCR(hs->head); + hs->n--; + } + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + l = 0; + switch (hs->kind) { + case HID_MOUSE: + if (len > l) { + buf[l++] = b; + } + if (len > l) { + buf[l++] = dx; + } + if (len > l) { + buf[l++] = dy; + } + if (len > l) { + buf[l++] = dz; + } + break; + + case HID_TABLET: + if (len > l) { + buf[l++] = b; + } + if (len > l) { + buf[l++] = dx & 0xff; + } + if (len > l) { + buf[l++] = dx >> 8; + } + if (len > l) { + buf[l++] = dy & 0xff; + } + if (len > l) { + buf[l++] = dy >> 8; + } + if (len > l) { + buf[l++] = dz; + } + break; + + default: + abort(); + } + + return l; +} + +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) +{ + if (len < 2) { + return 0; + } + + hid_keyboard_process_keycode(hs); + + buf[0] = hs->kbd.modifiers & 0xff; + buf[1] = 0; + if (hs->kbd.keys > 6) { + memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); + } else { + memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); + } + + return MIN(8, len); +} + +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) +{ + if (len > 0) { + int ledstate = 0; + /* 0x01: Num Lock LED + * 0x02: Caps Lock LED + * 0x04: Scroll Lock LED + * 0x08: Compose LED + * 0x10: Kana LED */ + hs->kbd.leds = buf[0]; + if (hs->kbd.leds & 0x04) { + ledstate |= QEMU_SCROLL_LOCK_LED; + } + if (hs->kbd.leds & 0x01) { + ledstate |= QEMU_NUM_LOCK_LED; + } + if (hs->kbd.leds & 0x02) { + ledstate |= QEMU_CAPS_LOCK_LED; + } + kbd_put_ledstate(ledstate); + } + return 0; +} + +void hid_reset(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_add_kbd_event_handler(hid_keyboard_event, hs); + memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); + memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); + hs->kbd.keys = 0; + break; + case HID_MOUSE: + case HID_TABLET: + memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); + break; + } + hs->head = 0; + hs->n = 0; +} + +void hid_free(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_remove_kbd_event_handler(); + break; + case HID_MOUSE: + case HID_TABLET: + qemu_remove_mouse_event_handler(hs->ptr.eh_entry); + break; + } +} + +void hid_init(HIDState *hs, int kind, HIDEventFunc event) +{ + hs->kind = kind; + hs->event = event; + + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 1, "QEMU HID Tablet"); + } +} diff --git a/hw/hid.h b/hw/hid.h new file mode 100644 index 0000000000..99910c3a86 --- /dev/null +++ b/hw/hid.h @@ -0,0 +1,54 @@ +#ifndef QEMU_HID_H +#define QEMU_HID_H + +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 + +typedef struct HIDPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} HIDPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; + int mouse_grabbed; + QEMUPutMouseEntry *eh_entry; +} HIDMouseState; + +typedef struct HIDKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; + uint16_t modifiers; + uint8_t leds; + uint8_t key[16]; + int32_t keys; +} HIDKeyboardState; + +struct HIDState { + union { + HIDMouseState ptr; + HIDKeyboardState kbd; + }; + uint32_t head; /* index into circular queue */ + uint32_t n; + int kind; + HIDEventFunc event; +}; + +void hid_init(HIDState *hs, int kind, HIDEventFunc event); +void hid_reset(HIDState *hs); +void hid_free(HIDState *hs); + +bool hid_has_events(HIDState *hs); +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); + +#endif /* QEMU_HID_H */ diff --git a/hw/usb-hid.c b/hw/usb-hid.c index b730692e0e..48ce743988 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -27,6 +27,7 @@ #include "usb.h" #include "usb-desc.h" #include "qemu-timer.h" +#include "hid.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -41,47 +42,6 @@ #define USB_DT_REPORT 0x22 #define USB_DT_PHY 0x23 -#define HID_MOUSE 1 -#define HID_TABLET 2 -#define HID_KEYBOARD 3 - -typedef struct HIDPointerEvent { - int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ - int32_t dz, buttons_state; -} HIDPointerEvent; - -#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ -#define QUEUE_MASK (QUEUE_LENGTH-1u) -#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) - -typedef struct HIDState HIDState; -typedef void (*HIDEventFunc)(HIDState *s); - -typedef struct HIDMouseState { - HIDPointerEvent queue[QUEUE_LENGTH]; - int mouse_grabbed; - QEMUPutMouseEntry *eh_entry; -} HIDMouseState; - -typedef struct HIDKeyboardState { - uint32_t keycodes[QUEUE_LENGTH]; - uint16_t modifiers; - uint8_t leds; - uint8_t key[16]; - int32_t keys; -} HIDKeyboardState; - -struct HIDState { - union { - HIDMouseState ptr; - HIDKeyboardState kbd; - }; - uint32_t head; /* index into circular queue */ - uint32_t n; - int kind; - HIDEventFunc event; -}; - typedef struct USBHIDState { USBDevice dev; HIDState hid; @@ -401,53 +361,6 @@ static const uint8_t qemu_keyboard_hid_report_descriptor[] = { 0xc0, /* End Collection */ }; -#define USB_HID_USAGE_ERROR_ROLLOVER 0x01 -#define USB_HID_USAGE_POSTFAIL 0x02 -#define USB_HID_USAGE_ERROR_UNDEFINED 0x03 - -/* Indices are QEMU keycodes, values are from HID Usage Table. Indices - * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ -static const uint8_t usb_hid_usage_keys[0x100] = { - 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, - 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, - 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, - 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, - 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, - 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, - 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, - 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, - 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, - 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, - 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, - 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, - 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, - 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, - 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static bool hid_has_events(HIDState *hs) -{ - return hs->n > 0; -} - static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); @@ -459,290 +372,11 @@ static void usb_hid_changed(HIDState *hs) usb_wakeup(&us->dev); } -static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) -{ - e->xdx = e->ydy = e->dz = 0; - e->buttons_state = buttons; -} - -static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, - int x1, int y1, int z1) { - if (xyrel) { - e->xdx += x1; - e->ydy += y1; - } else { - e->xdx = x1; - e->ydy = y1; - /* Windows drivers do not like the 0/0 position and ignore such - * events. */ - if (!(x1 | y1)) { - x1 = 1; - } - } - e->dz += z1; -} - -static void hid_pointer_event(void *opaque, - int x1, int y1, int z1, int buttons_state) -{ - HIDState *hs = opaque; - unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; - unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; - - /* We combine events where feasible to keep the queue small. We shouldn't - * combine anything with the first event of a particular button state, as - * that would change the location of the button state change. When the - * queue is empty, a second event is needed because we don't know if - * the first event changed the button state. */ - if (hs->n == QUEUE_LENGTH) { - /* Queue full. Discard old button state, combine motion normally. */ - hs->ptr.queue[use_slot].buttons_state = buttons_state; - } else if (hs->n < 2 || - hs->ptr.queue[use_slot].buttons_state != buttons_state || - hs->ptr.queue[previous_slot].buttons_state != - hs->ptr.queue[use_slot].buttons_state) { - /* Cannot or should not combine, so add an empty item to the queue. */ - QUEUE_INCR(use_slot); - hs->n++; - hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); - } - hid_pointer_event_combine(&hs->ptr.queue[use_slot], - hs->kind == HID_MOUSE, - x1, y1, z1); - hs->event(hs); -} - -static void hid_keyboard_event(void *opaque, int keycode) -{ - HIDState *hs = opaque; - int slot; - - if (hs->n == QUEUE_LENGTH) { - fprintf(stderr, "usb-kbd: warning: key event queue full\n"); - return; - } - slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - hs->kbd.keycodes[slot] = keycode; - hs->event(hs); -} - -static void hid_keyboard_process_keycode(HIDState *hs) -{ - uint8_t hid_code, key; - int i, keycode, slot; - - if (hs->n == 0) { - return; - } - slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; - keycode = hs->kbd.keycodes[slot]; - - key = keycode & 0x7f; - hid_code = usb_hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; - hs->kbd.modifiers &= ~(1 << 8); - - switch (hid_code) { - case 0x00: - return; - - case 0xe0: - if (hs->kbd.modifiers & (1 << 9)) { - hs->kbd.modifiers ^= 3 << 8; - return; - } - case 0xe1 ... 0xe7: - if (keycode & (1 << 7)) { - hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); - return; - } - case 0xe8 ... 0xef: - hs->kbd.modifiers |= 1 << (hid_code & 0x0f); - return; - } - - if (keycode & (1 << 7)) { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; - hs->kbd.key[hs->kbd.keys] = 0x00; - break; - } - } - if (i < 0) { - return; - } - } else { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - break; - } - } - if (i < 0) { - if (hs->kbd.keys < sizeof(hs->kbd.key)) { - hs->kbd.key[hs->kbd.keys++] = hid_code; - } - } else { - return; - } - } -} - -static inline int int_clamp(int val, int vmin, int vmax) -{ - if (val < vmin) - return vmin; - else if (val > vmax) - return vmax; - else - return val; -} - -static int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) -{ - int dx, dy, dz, b, l; - int index; - HIDPointerEvent *e; - - if (!hs->ptr.mouse_grabbed) { - qemu_activate_mouse_event_handler(hs->ptr.eh_entry); - hs->ptr.mouse_grabbed = 1; - } - - /* When the buffer is empty, return the last event. Relative - movements will all be zero. */ - index = (hs->n ? hs->head : hs->head - 1); - e = &hs->ptr.queue[index & QUEUE_MASK]; - - if (hs->kind == HID_MOUSE) { - dx = int_clamp(e->xdx, -127, 127); - dy = int_clamp(e->ydy, -127, 127); - e->xdx -= dx; - e->ydy -= dy; - } else { - dx = e->xdx; - dy = e->ydy; - } - dz = int_clamp(e->dz, -127, 127); - e->dz -= dz; - - b = 0; - if (e->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (e->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (e->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; - - if (hs->n && - !e->dz && - (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { - /* that deals with this event */ - QUEUE_INCR(hs->head); - hs->n--; - } - - /* Appears we have to invert the wheel direction */ - dz = 0 - dz; - l = 0; - switch (hs->kind) { - case HID_MOUSE: - if (len > l) - buf[l++] = b; - if (len > l) - buf[l++] = dx; - if (len > l) - buf[l++] = dy; - if (len > l) - buf[l++] = dz; - break; - - case HID_TABLET: - if (len > l) - buf[l++] = b; - if (len > l) - buf[l++] = dx & 0xff; - if (len > l) - buf[l++] = dx >> 8; - if (len > l) - buf[l++] = dy & 0xff; - if (len > l) - buf[l++] = dy >> 8; - if (len > l) - buf[l++] = dz; - break; - - default: - abort(); - } - - return l; -} - -static int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) -{ - if (len < 2) - return 0; - - hid_keyboard_process_keycode(hs); - - buf[0] = hs->kbd.modifiers & 0xff; - buf[1] = 0; - if (hs->kbd.keys > 6) { - memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - } else { - memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); - } - - return MIN(8, len); -} - -static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) -{ - if (len > 0) { - int ledstate = 0; - /* 0x01: Num Lock LED - * 0x02: Caps Lock LED - * 0x04: Scroll Lock LED - * 0x08: Compose LED - * 0x10: Kana LED */ - hs->kbd.leds = buf[0]; - if (hs->kbd.leds & 0x04) { - ledstate |= QEMU_SCROLL_LOCK_LED; - } - if (hs->kbd.leds & 0x01) { - ledstate |= QEMU_NUM_LOCK_LED; - } - if (hs->kbd.leds & 0x02) { - ledstate |= QEMU_CAPS_LOCK_LED; - } - kbd_put_ledstate(ledstate); - } - return 0; -} - -static void hid_handle_reset(HIDState *hs) -{ - switch (hs->kind) { - case HID_KEYBOARD: - qemu_add_kbd_event_handler(hid_keyboard_event, hs); - memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); - memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); - hs->kbd.keys = 0; - break; - case HID_MOUSE: - case HID_TABLET: - memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); - break; - } - hs->head = 0; - hs->n = 0; -} - static void usb_hid_handle_reset(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - hid_handle_reset(&us->hid); + hid_reset(&us->hid); us->protocol = 1; us->idle = 0; } @@ -875,19 +509,6 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return ret; } -static void hid_free(HIDState *hs) -{ - switch (hs->kind) { - case HID_KEYBOARD: - qemu_remove_kbd_event_handler(); - break; - case HID_MOUSE: - case HID_TABLET: - qemu_remove_mouse_event_handler(hs->ptr.eh_entry); - break; - } -} - static void usb_hid_handle_destroy(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); @@ -895,20 +516,6 @@ static void usb_hid_handle_destroy(USBDevice *dev) hid_free(&us->hid); } -static void hid_init(HIDState *hs, int kind, HIDEventFunc event) -{ - hs->kind = kind; - hs->event = event; - - if (hs->kind == HID_MOUSE) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 0, "QEMU HID Mouse"); - } else if (hs->kind == HID_TABLET) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 1, "QEMU HID Tablet"); - } -} - static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); From b069d3488f327da97fb0da1939518a00637f01a3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 15:52:33 +0200 Subject: [PATCH 15/16] hid: move idle+protocol from usb-hid to hid too. Signed-off-by: Gerd Hoffmann --- hw/hid.c | 8 ++++++++ hw/hid.h | 4 ++++ hw/usb-hid.c | 36 +++++++++++++----------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/hw/hid.c b/hw/hid.c index 1893ae59f0..7b5ef5fc92 100644 --- a/hw/hid.c +++ b/hw/hid.c @@ -24,6 +24,7 @@ */ #include "hw.h" #include "console.h" +#include "qemu-timer.h" #include "hid.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 @@ -73,6 +74,11 @@ bool hid_has_events(HIDState *hs) return hs->n > 0; } +void hid_set_next_idle(HIDState *hs, int64_t curtime) +{ + hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000; +} + static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) { e->xdx = e->ydy = e->dz = 0; @@ -365,6 +371,8 @@ void hid_reset(HIDState *hs) } hs->head = 0; hs->n = 0; + hs->protocol = 1; + hs->idle = 0; } void hid_free(HIDState *hs) diff --git a/hw/hid.h b/hw/hid.h index 99910c3a86..4a8fa5b63f 100644 --- a/hw/hid.h +++ b/hw/hid.h @@ -39,6 +39,9 @@ struct HIDState { uint32_t head; /* index into circular queue */ uint32_t n; int kind; + int32_t protocol; + uint8_t idle; + int64_t next_idle_clock; HIDEventFunc event; }; @@ -47,6 +50,7 @@ void hid_reset(HIDState *hs); void hid_free(HIDState *hs); bool hid_has_events(HIDState *hs); +void hid_set_next_idle(HIDState *hs, int64_t curtime); int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 48ce743988..e5d57de888 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -45,9 +45,6 @@ typedef struct USBHIDState { USBDevice dev; HIDState hid; - int32_t protocol; - uint8_t idle; - int64_t next_idle_clock; void *datain_opaque; void (*datain)(void *); } USBHIDState; @@ -377,13 +374,6 @@ static void usb_hid_handle_reset(USBDevice *dev) USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); hid_reset(&us->hid); - us->protocol = 1; - us->idle = 0; -} - -static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) -{ - s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000; } static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, @@ -448,22 +438,22 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, goto fail; } ret = 1; - data[0] = us->protocol; + data[0] = hs->protocol; break; case SET_PROTOCOL: if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; } ret = 0; - us->protocol = value; + hs->protocol = value; break; case GET_IDLE: ret = 1; - data[0] = us->idle; + data[0] = hs->idle; break; case SET_IDLE: - us->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(us, qemu_get_clock_ns(vm_clock)); + hs->idle = (uint8_t) (value >> 8); + hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -486,10 +476,10 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); if (!hid_has_events(hs) && - (!us->idle || us->next_idle_clock - curtime > 0)) { + (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; } - usb_hid_set_next_idle(us, curtime); + hid_set_next_idle(hs, curtime); if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { ret = hid_pointer_poll(hs, buf, p->iov.size); } else if (hs->kind == HID_KEYBOARD) { @@ -552,8 +542,8 @@ static int usb_hid_post_load(void *opaque, int version_id) { USBHIDState *s = opaque; - if (s->idle) { - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + if (s->hid.idle) { + hid_set_next_idle(&s->hid, qemu_get_clock_ns(vm_clock)); } return 0; } @@ -581,8 +571,8 @@ static const VMStateDescription vmstate_usb_ptr = { vmstate_usb_ptr_queue, HIDPointerEvent), VMSTATE_UINT32(hid.head, USBHIDState), VMSTATE_UINT32(hid.n, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; @@ -601,8 +591,8 @@ static const VMStateDescription vmstate_usb_kbd = { VMSTATE_UINT8(hid.kbd.leds, USBHIDState), VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16), VMSTATE_INT32(hid.kbd.keys, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; From fb8f4ceeb837a7410e018619daae6bc821060503 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 16:14:59 +0200 Subject: [PATCH 16/16] bluetooth: kill dummy usb device, use hid code directly. Signed-off-by: Gerd Hoffmann --- hw/bt-hid.c | 62 +++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/hw/bt-hid.c b/hw/bt-hid.c index a4204f98e7..5f1afe3e89 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -19,7 +19,9 @@ */ #include "qemu-common.h" -#include "usb.h" +#include "qemu-timer.h" +#include "console.h" +#include "hid.h" #include "bt.h" enum hid_transaction_req { @@ -86,7 +88,7 @@ struct bt_hid_device_s { struct bt_l2cap_device_s btdev; struct bt_l2cap_conn_params_s *control; struct bt_l2cap_conn_params_s *interrupt; - USBDevice *usbdev; + HIDState hid; int proto; int connected; @@ -111,7 +113,7 @@ static void bt_hid_reset(struct bt_hid_device_s *s) bt_l2cap_device_done(&s->btdev); bt_l2cap_device_init(&s->btdev, net); - s->usbdev->info->handle_reset(s->usbdev); + hid_reset(&s->hid); s->proto = BT_HID_PROTO_REPORT; s->state = bt_state_ready; s->dataother.len = 0; @@ -124,23 +126,16 @@ static void bt_hid_reset(struct bt_hid_device_s *s) static int bt_hid_out(struct bt_hid_device_s *s) { - USBPacket p; - if (s->data_type == BT_DATA_OUTPUT) { - usb_packet_init(&p); - usb_packet_setup(&p, USB_TOKEN_OUT, 0, 1); - usb_packet_addbuf(&p, s->dataout.buffer, s->dataout.len); - s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); - usb_packet_cleanup(&p); - - return s->dataout.len; + /* nothing */ + ; } if (s->data_type == BT_DATA_FEATURE) { /* XXX: * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE * or a SET_REPORT? */ - p.devep = 0; + ; } return -1; @@ -148,14 +143,8 @@ static int bt_hid_out(struct bt_hid_device_s *s) static int bt_hid_in(struct bt_hid_device_s *s) { - USBPacket p; - - usb_packet_init(&p); - usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); - usb_packet_addbuf(&p, s->dataout.buffer, sizeof(s->datain.buffer)); - s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); - usb_packet_cleanup(&p); - + s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, + sizeof(s->datain.buffer)); return s->datain.len; } @@ -323,8 +312,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } s->proto = parameter; - s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0, - NULL); + s->hid.protocol = parameter; ret = BT_HS_SUCCESSFUL; break; @@ -333,8 +321,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, ret = BT_HS_ERR_INVALID_PARAMETER; break; } - s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1, - s->control->sdu_out(s->control, 1)); + *s->control->sdu_out(s->control, 1) = s->hid.idle; s->control->sdu_submit(s->control); break; @@ -344,11 +331,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } - /* We don't need to know about the Idle Rate here really, - * so just pass it on to the device. */ - ret = s->usbdev->info->handle_control(s->usbdev, NULL, - SET_IDLE, data[1], 0, 0, NULL) ? - BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; + s->hid.idle = data[1]; /* XXX: Does this generate a handshake? */ break; @@ -385,9 +368,10 @@ static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) bt_hid_control_transaction(hid, data, len); } -static void bt_hid_datain(void *opaque) +static void bt_hid_datain(HIDState *hs) { - struct bt_hid_device_s *hid = opaque; + struct bt_hid_device_s *hid = + container_of(hs, struct bt_hid_device_s, hid); /* If suspended, wake-up and send a wake-up event first. We might * want to also inspect the input report and ignore event like @@ -450,7 +434,7 @@ static void bt_hid_connected_update(struct bt_hid_device_s *hid) hid->btdev.device.inquiry_scan = !hid->connected; if (hid->connected && !prev) { - hid->usbdev->info->handle_reset(hid->usbdev); + hid_reset(&hid->hid); hid->proto = BT_HID_PROTO_REPORT; } @@ -518,7 +502,7 @@ static void bt_hid_destroy(struct bt_device_s *dev) bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); bt_l2cap_device_done(&hid->btdev); - hid->usbdev->info->handle_destroy(hid->usbdev); + hid_free(&hid->hid); qemu_free(hid); } @@ -531,7 +515,7 @@ enum peripheral_minor_class { }; static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - USBDevice *dev, enum peripheral_minor_class minor) + enum peripheral_minor_class minor) { struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); uint32_t class = @@ -551,9 +535,8 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, BT_HID_MTU, bt_hid_new_interrupt_ch); - s->usbdev = dev; - s->btdev.device.lmp_name = s->usbdev->product_desc; - usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); + hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); + s->btdev.device.lmp_name = "BT Keyboard"; s->btdev.device.handle_destroy = bt_hid_destroy; @@ -566,6 +549,5 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) { - USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd"); - return bt_hid_init(net, dev, class_keyboard); + return bt_hid_init(net, class_keyboard); }