Commit aefcd283 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kraxel/tags/usb-20190130-pull-request' into staging



usb: xhci: fix iso transfers.
usb: mtp: break up writes, bugfixes.
usb: fix lgpl info in headers.
usb: hid: unique serials.

# gpg: Signature made Wed 30 Jan 2019 07:33:21 GMT
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/usb-20190130-pull-request:
  usb-mtp: replace the homebrew write with qemu_write_full
  usb-mtp: breakup MTP write into smaller chunks
  usb-mtp: Reallocate buffer in multiples of MTP_WRITE_BUF_SZ
  usb: implement XHCI underrun/overrun events
  usb: XHCI shall not halt isochronous endpoints
  hw/usb: Fix LGPL information in the file headers
  usb: dev-mtp: close fd in usb_mtp_object_readdir()
  usb: assign unique serial numbers to hid devices

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 460da100 49f9e8d6
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);

+2 −2
Original line number Diff line number Diff line
@@ -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 <http://www.gnu.org/licenses/>.
 */
#include "qemu/osdep.h"
+15 −11
Original line number Diff line number Diff line
@@ -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);
+104 −64
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "trace.h"
#include "hw/usb.h"
#include "desc.h"
#include "qemu/units.h"

/* ----------------------------------------------------------------------- */

@@ -35,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,
@@ -152,8 +160,10 @@ struct MTPData {
    bool         first;
    /* Used for >4G file sizes */
    bool         pending;
    uint64_t     cached_length;
    int          fd;
    uint8_t      write_status;
    /* Internal pointer per every MTP_WRITE_BUF_SZ */
    uint64_t     data_offset;
};

struct MTPObject {
@@ -244,6 +254,7 @@ typedef struct {

#define MTP_MANUFACTURER  "QEMU"
#define MTP_PRODUCT       "QEMU filesharing"
#define MTP_WRITE_BUF_SZ  (512 * KiB)

enum {
    STR_MANUFACTURER = 1,
@@ -666,6 +677,7 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
    }
    dir = fdopendir(fd);
    if (!dir) {
        close(fd);
        return;
    }
#ifdef CONFIG_INOTIFY1
@@ -1618,21 +1630,28 @@ 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;
        uint64_t ret = 0;

        while (bytes_left > 0) {
                ret = write(fd, buf, bytes_left);
                if ((ret == -1) && (errno != EINTR || errno != EAGAIN ||
                                    errno != EWOULDBLOCK)) {
                        break;
        if (lseek(fd, offset, SEEK_SET) < 0) {
            goto done;
        }
                bytes_left -= ret;
                buf += ret;

        ret = qemu_write_full(fd, buf, size);

done:
        return ret;
}

        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)
@@ -1646,7 +1665,9 @@ static void usb_mtp_write_data(MTPState *s)

    assert(d != NULL);

    if (parent == NULL || !s->write_pending) {
    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;
@@ -1658,37 +1679,43 @@ 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)) {
            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);
            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
         */
            /* Return success if initiator sent 0 sized data */
            if (!s->dataset.size) {
                goto success;
            }

        rc = write_retry(d->fd, d->data, d->offset);
        if (rc != d->offset) {
            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 && rc != s->dataset.size) {
            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;
            }
            usb_mtp_update_object(parent, s->dataset.filename);
        }
    }

success:
@@ -1776,37 +1803,43 @@ 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);
        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;
        d->data_offset = 0;
        d->write_status = WRITE_START;
    }

    if (d->pending) {
        usb_mtp_realloc(d, d->cached_length);
        d->length += d->cached_length;
        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;
        /* 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;
        }
        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 +1850,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->length));
                   (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:
+2 −2
Original line number Diff line number Diff line
@@ -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 <http://www.gnu.org/licenses/>.
 */

Loading