diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-08-21 18:00:27 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-08-21 18:00:27 +0100 |
commit | 78ee443e52907ec540e3e40edbf8456a3b0ec14b (patch) | |
tree | e037142144a143c2bcba725f6207ab6dcca35597 | |
parent | 13b7b188501d419a7d63c016e00065bcc693b7d4 (diff) | |
parent | 15aa757d0523b9d6fb26ea1e5c967ba0619dce0a (diff) |
Merge remote-tracking branch 'remotes/kraxel/tags/usb-20180821-pull-request' into staging
usb: mtp write support, bugfixes.
# gpg: Signature made Tue 21 Aug 2018 10:11:36 BST
# gpg: using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138
* remotes/kraxel/tags/usb-20180821-pull-request:
dev-mtp: rename x-root to rootdir
dev-mtp: Add support for > 4GB file transfers
dev-mtp: retry write for incomplete transfers
dev-mtp: fix buffer allocation for writing file contents
dev-mtp: add support for canceling transaction
ohci: Clear the interrupt counter for erroneous transfers
docs/usb2.txt: ehci has six ports
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | docs/usb2.txt | 4 | ||||
-rw-r--r-- | hw/usb/dev-mtp.c | 93 | ||||
-rw-r--r-- | hw/usb/hcd-ohci.c | 3 |
3 files changed, 84 insertions, 16 deletions
diff --git a/docs/usb2.txt b/docs/usb2.txt index f63c8d9465..172614d3a7 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -94,8 +94,8 @@ physical port addressing First you can (for all USB devices) specify the physical port where the device will show up in the guest. This can be done using the -"port" property. UHCI has two root ports (1,2). EHCI has four root -ports (1-4), the emulated (1.1) USB hub has eight ports. +"port" property. UHCI has two root ports (1,2). EHCI has six root +ports (1-6), the emulated (1.1) USB hub has eight ports. Plugging a tablet into UHCI port 1 works like this: diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 1ded7ac9a3..3fdc4b0da1 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -82,6 +82,7 @@ enum mtp_code { FMT_ASSOCIATION = 0x3001, /* event codes */ + EVT_CANCEL_TRANSACTION = 0x4001, EVT_OBJ_ADDED = 0x4002, EVT_OBJ_REMOVED = 0x4003, EVT_OBJ_INFO_CHANGED = 0x4007, @@ -146,9 +147,12 @@ struct MTPData { uint32_t trans; uint64_t offset; uint64_t length; - uint32_t alloc; + uint64_t alloc; uint8_t *data; bool first; + /* Used for >4G file sizes */ + bool pending; + uint64_t cached_length; int fd; }; @@ -1551,14 +1555,35 @@ static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, int length, uint8_t *data) { int ret; + MTPState *s = USB_MTP(dev); + uint16_t *event = (uint16_t *)data; - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; + switch (request) { + case ClassInterfaceOutRequest | 0x64: + if (*event == EVT_CANCEL_TRANSACTION) { + g_free(s->result); + s->result = NULL; + usb_mtp_data_free(s->data_in); + s->data_in = NULL; + if (s->write_pending) { + g_free(s->dataset.filename); + s->write_pending = false; + } + usb_mtp_data_free(s->data_out); + s->data_out = NULL; + } else { + p->status = USB_RET_STALL; + } + break; + default: + ret = usb_desc_handle_control(dev, p, request, + value, index, length, data); + if (ret >= 0) { + return; + } } trace_usb_mtp_stall(dev->addr, "unknown control request"); - p->status = USB_RET_STALL; } static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) @@ -1580,13 +1605,31 @@ static void utf16_to_str(uint8_t len, uint16_t *arr, char *name) g_free(wstr); } +/* Wrapper around write, returns 0 on failure */ +static uint64_t write_retry(int fd, void *buf, uint64_t size) +{ + uint64_t bytes_left = size, ret; + + while (bytes_left > 0) { + ret = write(fd, buf, bytes_left); + if ((ret == -1) && (errno != EINTR || errno != EAGAIN || + errno != EWOULDBLOCK)) { + break; + } + bytes_left -= ret; + buf += ret; + } + + return size - bytes_left; +} + static void usb_mtp_write_data(MTPState *s) { MTPData *d = s->data_out; MTPObject *parent = usb_mtp_object_lookup(s, s->dataset.parent_handle); char *path = NULL; - int rc = -1; + uint64_t rc; mode_t mask = 0644; assert(d != NULL); @@ -1603,7 +1646,7 @@ static void usb_mtp_write_data(MTPState *s) d->fd = mkdir(path, mask); goto free; } - if (s->dataset.size < d->length) { + if ((s->dataset.size != 0xFFFFFFFF) && (s->dataset.size < d->length)) { usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); goto done; @@ -1622,8 +1665,8 @@ static void usb_mtp_write_data(MTPState *s) goto success; } - rc = write(d->fd, d->data, s->dataset.size); - if (rc == -1) { + rc = write_retry(d->fd, d->data, s->dataset.size); + if (!rc) { usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); goto done; @@ -1699,6 +1742,7 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, MTPData *d = s->data_out; uint64_t dlen; uint32_t data_len = p->iov.size; + uint64_t total_len; if (!d) { usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, @@ -1707,18 +1751,33 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, } if (d->first) { /* Total length of incoming data */ - d->length = cpu_to_le32(container->length) - sizeof(mtp_container); + total_len = cpu_to_le32(container->length) - sizeof(mtp_container); /* Length of data in this packet */ data_len -= sizeof(mtp_container); - usb_mtp_realloc(d, d->length); + usb_mtp_realloc(d, total_len); + d->length += total_len; d->offset = 0; + d->cached_length = total_len; d->first = false; + d->pending = false; + } + + if (d->pending) { + usb_mtp_realloc(d, d->cached_length); + d->length += d->cached_length; + d->pending = false; } if (d->length - d->offset > data_len) { dlen = data_len; } else { dlen = d->length - d->offset; + /* Check for cached data for large files */ + if ((s->dataset.size == 0xFFFFFFFF) && (dlen < p->iov.size)) { + usb_mtp_realloc(d, p->iov.size - dlen); + d->length += p->iov.size - dlen; + dlen = p->iov.size; + } } switch (d->code) { @@ -1738,12 +1797,18 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, case CMD_SEND_OBJECT: usb_packet_copy(p, d->data + d->offset, dlen); d->offset += dlen; - if (d->offset == d->length) { + if ((p->iov.size % 64) || !p->iov.size) { + assert((s->dataset.size == 0xFFFFFFFF) || + (s->dataset.size == d->length)); + usb_mtp_write_data(s); usb_mtp_data_free(s->data_out); s->data_out = NULL; return; } + if (d->offset == d->length) { + d->pending = true; + } break; default: p->status = USB_RET_STALL; @@ -1953,7 +2018,7 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) QTAILQ_INIT(&s->objects); if (s->desc == NULL) { if (s->root == NULL) { - error_setg(errp, "usb-mtp: x-root property must be configured"); + error_setg(errp, "usb-mtp: rootdir property must be configured"); return; } s->desc = strrchr(s->root, '/'); @@ -1982,7 +2047,7 @@ static const VMStateDescription vmstate_usb_mtp = { }; static Property mtp_properties[] = { - DEFINE_PROP_STRING("x-root", MTPState, root), + DEFINE_PROP_STRING("rootdir", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index d4c0293db5..98da5f0f04 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1156,6 +1156,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } + /* An error occured so we have to clear the interrupt counter. See + * spec at 6.4.4 on page 104 */ + ohci->done_count = 0; } ed->head |= OHCI_ED_H; } |