From b63e10508bee7169cfc7022805638c7358feefb5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 10 Jan 2019 13:51:08 +0100 Subject: usb: assign unique serial numbers to hid devices Windows guests have trouble dealing with usb devices having identical serial numbers. So, assign unique serial numbers to usb hid devices. All other usb devices have this already. In the past the fixed serial number has been used to indicate working remote setup to linux guests. Here is a bit of history: * First there was nothing. * Then I added a rule to udev checking for serial == 42. (this is in rhel-6). * Then systemd + udev merged. * Then I changed the rule to check for serial != 1 instead, so we can use any serial but "1" which is the one the old broken devices had (this is in rhel-7). March 2014 in upstream systemd. * Then all usb power management rules where dropped from systemd (June 2015). Which I figured today (Sept 2018), after wondering that the rules are gone in fedora 28. So, three years ago the serial number check was dropped upstream, yet I hav't seen a single report about autosuspend issues (or cpu usage for usb emulation going up, which is the typical symtom). So I figured I can stop worring that changing the serial number will break things and just do it. And even if it turns out autosuspend is still an issue: I think meanwhile we can really stop worrying about guests running in old qemu versions with broken usb suspend (fixed in 0.13 !). If needed we can enable autosuspend unconditionally in guests. Signed-off-by: Gerd Hoffmann Message-id: 20190110125108.22834-1-kraxel@redhat.com --- hw/core/machine.c | 3 +++ hw/usb/dev-hid.c | 26 +++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 2629515..077fbd1 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -30,6 +30,9 @@ GlobalProperty hw_compat_3_1[] = { { "memory-backend-memfd", "x-use-canonical-path-for-ramblock-id", "true" }, { "tpm-crb", "ppi", "false" }, { "tpm-tis", "ppi", "false" }, + { "usb-kbd", "serial", "42" }, + { "usb-mouse", "serial", "42" }, + { "usb-kbd", "serial", "42" }, }; const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 90cd745..f9ea303 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -61,10 +61,13 @@ enum { STR_PRODUCT_MOUSE, STR_PRODUCT_TABLET, STR_PRODUCT_KEYBOARD, - STR_SERIALNUMBER, + STR_SERIAL_COMPAT, STR_CONFIG_MOUSE, STR_CONFIG_TABLET, STR_CONFIG_KEYBOARD, + STR_SERIAL_MOUSE, + STR_SERIAL_TABLET, + STR_SERIAL_KEYBOARD, }; static const USBDescStrings desc_strings = { @@ -72,10 +75,13 @@ static const USBDescStrings desc_strings = { [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", [STR_PRODUCT_TABLET] = "QEMU USB Tablet", [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", - [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ + [STR_SERIAL_COMPAT] = "42", [STR_CONFIG_MOUSE] = "HID Mouse", [STR_CONFIG_TABLET] = "HID Tablet", [STR_CONFIG_KEYBOARD] = "HID Keyboard", + [STR_SERIAL_MOUSE] = "89126", + [STR_SERIAL_TABLET] = "28754", + [STR_SERIAL_KEYBOARD] = "68284", }; static const USBDescIface desc_iface_mouse = { @@ -375,7 +381,7 @@ static const USBDesc desc_mouse = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_MOUSE, }, .full = &desc_device_mouse, .str = desc_strings, @@ -389,7 +395,7 @@ static const USBDesc desc_mouse2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_MOUSE, }, .full = &desc_device_mouse, .high = &desc_device_mouse2, @@ -404,7 +410,7 @@ static const USBDesc desc_tablet = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_TABLET, }, .full = &desc_device_tablet, .str = desc_strings, @@ -418,7 +424,7 @@ static const USBDesc desc_tablet2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_TABLET, }, .full = &desc_device_tablet, .high = &desc_device_tablet2, @@ -433,7 +439,7 @@ static const USBDesc desc_keyboard = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_KEYBOARD, }, .full = &desc_device_keyboard, .str = desc_strings, @@ -447,7 +453,7 @@ static const USBDesc desc_keyboard2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_KEYBOARD, }, .full = &desc_device_keyboard, .high = &desc_device_keyboard2, @@ -718,9 +724,7 @@ static void usb_hid_initfn(USBDevice *dev, int kind, return; } - if (dev->serial) { - usb_desc_set_string(dev, STR_SERIALNUMBER, dev->serial); - } + usb_desc_create_serial(dev); usb_desc_init(dev); us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); hid_init(&us->hid, kind, usb_hid_changed); -- cgit v1.1 From 6e3c1a68f91d8de2a800d994edf0c9eedde6f090 Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Thu, 3 Jan 2019 05:31:13 -0800 Subject: usb: dev-mtp: close fd in usb_mtp_object_readdir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spotted by Coverity: CID 1397070 Signed-off-by: Li Qiang Reviewed-by: Philippe Mathieu-Daudé Message-id: 20190103133113.49599-1-liq3ea@163.com [ kraxel: dropped chunk which adds close() after successful fdopendir() call, that is not needed according to POSIX even though Coverity flags it as bug ] Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 68c5eb8..837c9d9 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -666,6 +666,7 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) } dir = fdopendir(fd); if (!dir) { + close(fd); return; } #ifdef CONFIG_INOTIFY1 -- cgit v1.1 From 75a49fc61ab32a341de00bae6295603f4a08f14e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 23 Jan 2019 15:40:54 +0100 Subject: hw/usb: Fix LGPL information in the file headers It's either "GNU *Library* General Public version 2" or "GNU Lesser General Public version *2.1*", but there was no "version 2.0" of the "Lesser" library. So assume that version 2.1 is meant here. Additionally, suggest that the user should have received a copy of the LGPL, and not the GPL here. Signed-off-by: Thomas Huth Message-id: 1548254454-7659-1-git-send-email-thuth@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/combined-packet.c | 4 ++-- hw/usb/hcd-ehci-pci.c | 4 ++-- hw/usb/hcd-ehci-sysbus.c | 4 ++-- hw/usb/hcd-ehci.c | 5 ++--- hw/usb/hcd-ehci.h | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index fc98383..37b23e2 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -9,14 +9,14 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "qemu/osdep.h" diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 69abbf7..38b24b1 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -4,14 +4,14 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 331faf8..9f7f128 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -4,14 +4,14 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e233681..9b132cb 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -12,18 +12,17 @@ * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, * Jan Kiszka and Vincent Palatin contributed bugfixes. * - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index d660170..fedf82c 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -4,14 +4,14 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ -- cgit v1.1 From a587c832a3f1d6d47dce93bda52c80cfa163e7cf Mon Sep 17 00:00:00 2001 From: Yuri Benditovich Date: Mon, 28 Jan 2019 20:05:07 +0000 Subject: usb: XHCI shall not halt isochronous endpoints According to the XHCI spec (4.10.2) the controller never halts isochronous endpoints. This commit prevent stop of isochronous streaming when sporadic errors status received from backends. Signed-off-by: Yuri Benditovich Message-id: 20190128200444.5128-2-yuri.benditovich@janustech.com Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 8f1a01a..1a8fd96 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1571,6 +1571,11 @@ static void xhci_stall_ep(XHCITransfer *xfer) uint32_t err; XHCIStreamContext *sctx; + if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { + /* never halt isoch endpoints, 4.10.2 */ + return; + } + if (epctx->nr_pstreams) { sctx = xhci_find_stream(epctx, xfer->streamid, &err); if (sctx == NULL) { -- cgit v1.1 From b4329d1a2a42d3c49ac4b76ec86b6c9db19ea1e9 Mon Sep 17 00:00:00 2001 From: Yuri Benditovich Date: Mon, 28 Jan 2019 20:05:09 +0000 Subject: usb: implement XHCI underrun/overrun events Implement underrun/overrun events of isochronous endpoints according to XHCI spec (4.10.3.1) Guest software restarts data streaming when receives these events. The XHCI reports these events using interrupter assigned to the slot (as these events do not have TRB), so current commit adds the field of assigned interrupter to the XHCISlot structure. Guest software assigns interrupter to the slot on 'Address Device' and 'Evaluate Context' commands. Signed-off-by: Yuri Benditovich Message-id: 20190128200444.5128-3-yuri.benditovich@janustech.com Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 17 +++++++++++++++-- hw/usb/hcd-xhci.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 1a8fd96..19c64f7 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1949,6 +1949,16 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) while (1) { length = xhci_ring_chain_length(xhci, ring); if (length <= 0) { + if (epctx->type == ET_ISO_OUT || epctx->type == ET_ISO_IN) { + /* 4.10.3.1 */ + XHCIEvent ev = { ER_TRANSFER }; + ev.ccode = epctx->type == ET_ISO_IN ? + CC_RING_OVERRUN : CC_RING_UNDERRUN; + ev.slotid = epctx->slotid; + ev.epid = epctx->epid; + ev.ptr = epctx->ring.dequeue; + xhci_event(xhci, &ev, xhci->slots[epctx->slotid-1].intr); + } break; } xfer = xhci_ep_alloc_xfer(epctx, length); @@ -2028,6 +2038,7 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) xhci->slots[slotid-1].enabled = 0; xhci->slots[slotid-1].addressed = 0; xhci->slots[slotid-1].uport = NULL; + xhci->slots[slotid-1].intr = 0; return CC_SUCCESS; } @@ -2127,6 +2138,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot = &xhci->slots[slotid-1]; slot->uport = uport; slot->ctx = octx; + slot->intr = get_field(slot_ctx[2], TRB_INTR); /* Make sure device is in USB_STATE_DEFAULT state */ usb_device_reset(dev); @@ -2300,8 +2312,9 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid, slot_ctx[1] &= ~0xFFFF; /* max exit latency */ slot_ctx[1] |= islot_ctx[1] & 0xFFFF; - slot_ctx[2] &= ~0xFF00000; /* interrupter target */ - slot_ctx[2] |= islot_ctx[2] & 0xFF000000; + /* update interrupter target field */ + xhci->slots[slotid-1].intr = get_field(islot_ctx[2], TRB_INTR); + set_field(&slot_ctx[2], xhci->slots[slotid-1].intr, TRB_INTR); DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index fc36a4c..240caa4 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -140,6 +140,7 @@ typedef struct XHCIPort { typedef struct XHCISlot { bool enabled; bool addressed; + uint16_t intr; dma_addr_t ctx; USBPort *uport; XHCIEPContext *eps[31]; -- cgit v1.1 From 179fcf8a83a91c74e323b1fe1b5397520a8af047 Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Tue, 29 Jan 2019 08:19:06 -0500 Subject: usb-mtp: Reallocate buffer in multiples of MTP_WRITE_BUF_SZ This is a "pre-patch" to breaking up the write buffer for MTP writes. Instead of allocating a mtp buffer equal to size sent by the initiator, we start with a small size and reallocate multiples (of that small size) as needed. Signed-off-by: Bandan Das Message-id: 20190129131908.27924-2-bsd@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 837c9d9..2e342c7 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -25,6 +25,7 @@ #include "trace.h" #include "hw/usb.h" #include "desc.h" +#include "qemu/units.h" /* ----------------------------------------------------------------------- */ @@ -152,7 +153,6 @@ struct MTPData { bool first; /* Used for >4G file sizes */ bool pending; - uint64_t cached_length; int fd; }; @@ -244,6 +244,7 @@ typedef struct { #define MTP_MANUFACTURER "QEMU" #define MTP_PRODUCT "QEMU filesharing" +#define MTP_WRITE_BUF_SZ (512 * KiB) enum { STR_MANUFACTURER = 1, @@ -1659,7 +1660,7 @@ static void usb_mtp_write_data(MTPState *s) d->fd = mkdir(path, mask); goto free; } - if ((s->dataset.size != 0xFFFFFFFF) && (s->dataset.size < d->length)) { + if ((s->dataset.size != 0xFFFFFFFF) && (s->dataset.size != d->offset)) { usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); goto done; @@ -1777,17 +1778,21 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *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, total_len); - d->length += total_len; + if (total_len < MTP_WRITE_BUF_SZ) { + usb_mtp_realloc(d, total_len); + d->length += total_len; + } else { + usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - sizeof(mtp_container)); + d->length += MTP_WRITE_BUF_SZ - sizeof(mtp_container); + } 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; + usb_mtp_realloc(d, MTP_WRITE_BUF_SZ); + d->length += MTP_WRITE_BUF_SZ; d->pending = false; } @@ -1795,12 +1800,6 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, 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) { @@ -1822,7 +1821,7 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, d->offset += dlen; if ((p->iov.size % 64) || !p->iov.size) { assert((s->dataset.size == 0xFFFFFFFF) || - (s->dataset.size == d->length)); + (s->dataset.size == d->offset)); usb_mtp_write_data(s); usb_mtp_data_free(s->data_out); -- cgit v1.1 From c1ef0f2519c706f738d76b9b68b6627c889bab14 Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Tue, 29 Jan 2019 08:19:07 -0500 Subject: usb-mtp: breakup MTP write into smaller chunks For every MTP_WRITE_BUF_SZ copied, this patch writes it to file before getting the next block of data. The file is kept opened for the duration of the operation but the sanity checks on the write operation are performed only once when the write operation starts. Additionally, we also update the file size in the object metadata once the file has completely been written. Suggested-by: Gerd Hoffman Signed-off-by: Bandan Das Message-id: 20190129131908.27924-3-bsd@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 134 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 43 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 2e342c7..2ca25c9 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -36,6 +36,13 @@ enum mtp_container_type { TYPE_EVENT = 4, }; +/* MTP write stage, for internal use only */ +enum mtp_write_status { + WRITE_START = 1, + WRITE_CONTINUE = 2, + WRITE_END = 3, +}; + enum mtp_code { /* command codes */ CMD_GET_DEVICE_INFO = 0x1001, @@ -154,6 +161,9 @@ struct MTPData { /* Used for >4G file sizes */ bool pending; int fd; + uint8_t write_status; + /* Internal pointer per every MTP_WRITE_BUF_SZ */ + uint64_t data_offset; }; struct MTPObject { @@ -1620,10 +1630,14 @@ static char *utf16_to_str(uint8_t len, uint16_t *arr) } /* Wrapper around write, returns 0 on failure */ -static uint64_t write_retry(int fd, void *buf, uint64_t size) +static uint64_t write_retry(int fd, void *buf, uint64_t size, off_t offset) { uint64_t bytes_left = size, ret; + if (lseek(fd, offset, SEEK_SET) < 0) { + goto done; + } + while (bytes_left > 0) { ret = write(fd, buf, bytes_left); if ((ret == -1) && (errno != EINTR || errno != EAGAIN || @@ -1634,9 +1648,20 @@ static uint64_t write_retry(int fd, void *buf, uint64_t size) buf += ret; } +done: return size - bytes_left; } +static void usb_mtp_update_object(MTPObject *parent, char *name) +{ + MTPObject *o = + usb_mtp_object_lookup_name(parent, name, strlen(name)); + + if (o) { + lstat(o->path, &o->stat); + } +} + static void usb_mtp_write_data(MTPState *s) { MTPData *d = s->data_out; @@ -1648,48 +1673,56 @@ static void usb_mtp_write_data(MTPState *s) assert(d != NULL); - if (parent == NULL || !s->write_pending) { - usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, - 0, 0, 0, 0); + switch (d->write_status) { + case WRITE_START: + if (!parent || !s->write_pending) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, + 0, 0, 0, 0); return; - } - - if (s->dataset.filename) { - path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); - if (s->dataset.format == FMT_ASSOCIATION) { - d->fd = mkdir(path, mask); - goto free; - } - if ((s->dataset.size != 0xFFFFFFFF) && (s->dataset.size != d->offset)) { - usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, - 0, 0, 0, 0); - goto done; - } - d->fd = open(path, O_CREAT | O_WRONLY | O_CLOEXEC | O_NOFOLLOW, mask); - if (d->fd == -1) { - usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, - 0, 0, 0, 0); - goto done; } - /* - * Return success if initiator sent 0 sized data - */ - if (!s->dataset.size) { - goto success; - } + if (s->dataset.filename) { + path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); + if (s->dataset.format == FMT_ASSOCIATION) { + d->fd = mkdir(path, mask); + goto free; + } + d->fd = open(path, O_CREAT | O_WRONLY | + O_CLOEXEC | O_NOFOLLOW, mask); + if (d->fd == -1) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + goto done; + } - rc = write_retry(d->fd, d->data, d->offset); - if (rc != d->offset) { + /* Return success if initiator sent 0 sized data */ + if (!s->dataset.size) { + goto success; + } + if (d->length != MTP_WRITE_BUF_SZ && !d->pending) { + d->write_status = WRITE_END; + } + } + /* fall through */ + case WRITE_CONTINUE: + case WRITE_END: + rc = write_retry(d->fd, d->data, d->data_offset, + d->offset - d->data_offset); + if (rc != d->data_offset) { usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); goto done; + } + if (d->write_status != WRITE_END) { + return; + } else { + /* Only for < 4G file sizes */ + if (s->dataset.size != 0xFFFFFFFF && d->offset != s->dataset.size) { + usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, + 0, 0, 0, 0); + goto done; } - /* Only for < 4G file sizes */ - if (s->dataset.size != 0xFFFFFFFF && rc != s->dataset.size) { - usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, - 0, 0, 0, 0); - goto done; + usb_mtp_update_object(parent, s->dataset.filename); } } @@ -1788,25 +1821,33 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, d->offset = 0; d->first = false; d->pending = false; + d->data_offset = 0; + d->write_status = WRITE_START; } if (d->pending) { - usb_mtp_realloc(d, MTP_WRITE_BUF_SZ); - d->length += MTP_WRITE_BUF_SZ; + memset(d->data, 0, d->length); + if (d->length != MTP_WRITE_BUF_SZ) { + usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - d->length); + d->length += (MTP_WRITE_BUF_SZ - d->length); + } d->pending = false; + d->write_status = WRITE_CONTINUE; + d->data_offset = 0; } - if (d->length - d->offset > data_len) { + if (d->length - d->data_offset > data_len) { dlen = data_len; } else { - dlen = d->length - d->offset; + dlen = d->length - d->data_offset; } switch (d->code) { case CMD_SEND_OBJECT_INFO: - usb_packet_copy(p, d->data + d->offset, dlen); + usb_packet_copy(p, d->data + d->data_offset, dlen); d->offset += dlen; - if (d->offset == d->length) { + d->data_offset += dlen; + if (d->data_offset == d->length) { /* The operation might have already failed */ if (!s->result) { usb_mtp_write_metadata(s, dlen); @@ -1817,19 +1858,26 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, } break; case CMD_SEND_OBJECT: - usb_packet_copy(p, d->data + d->offset, dlen); + usb_packet_copy(p, d->data + d->data_offset, dlen); d->offset += dlen; + d->data_offset += dlen; if ((p->iov.size % 64) || !p->iov.size) { assert((s->dataset.size == 0xFFFFFFFF) || (s->dataset.size == d->offset)); + if (d->length == MTP_WRITE_BUF_SZ) { + d->write_status = WRITE_END; + } else { + d->write_status = WRITE_START; + } usb_mtp_write_data(s); usb_mtp_data_free(s->data_out); s->data_out = NULL; return; } - if (d->offset == d->length) { + if (d->data_offset == d->length) { d->pending = true; + usb_mtp_write_data(s); } break; default: -- cgit v1.1 From 49f9e8d660d41cbe325d554e742d7100d59a7abf Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Tue, 29 Jan 2019 08:19:08 -0500 Subject: usb-mtp: replace the homebrew write with qemu_write_full qemu_write_full takes care of partial blocking writes, as in cases of larger file sizes Suggested-by: Peter Maydell Signed-off-by: Bandan Das Message-id: 20190129131908.27924-4-bsd@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 2ca25c9..f1d20fa 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1632,24 +1632,16 @@ static char *utf16_to_str(uint8_t len, uint16_t *arr) /* Wrapper around write, returns 0 on failure */ static uint64_t write_retry(int fd, void *buf, uint64_t size, off_t offset) { - uint64_t bytes_left = size, ret; + uint64_t ret = 0; if (lseek(fd, offset, SEEK_SET) < 0) { goto done; } - 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; - } + ret = qemu_write_full(fd, buf, size); done: - return size - bytes_left; + return ret; } static void usb_mtp_update_object(MTPObject *parent, char *name) -- cgit v1.1