usb: xhci: fix iso transfers.

usb: mtp: break up writes, bugfixes.
 usb: fix lgpl info in headers.
 usb: hid: unique serials.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJcUVNBAAoJEEy22O7T6HE4awkQAKk1eaOPj5/9GdU5mioCAbGM
 zaYMuBvjfJqLragSfKDHEX2k7tTpQjkp48AklVOiKF2GZly+AqvCxAUjja9H4W8x
 TrXbnLe5No4aX5tHUzHmfDocfgDkuTi2aysQrAmUcJjChG1rMwj+fczr82L0Pbc2
 1pNw0Z+0Cu+2i2WNawTF9tiRwJobrCACrTgdRMhgorfIR/TCKtgFlEnMzkXZIZ5k
 awf7GRmYYHCFQffQK4gENPEmYVjiHLB0kpIO4m8As5F0Ex49Lg0rbZiCh+GLy014
 HF0eEVlIS3EEKsu3BxgeJtLTtxvRC4h4o1esbBGcaClvdPaD2DOphuilCMXiY8KL
 v8Z7r5qqfAiBHBkUAEGlahwjpsD52POz+OYQ7FgsUbQJ4hN34zrI4t7+o4ThoDV7
 MTegpOiKxt5xqmj19+bkl6zR/hIafuJxUVOhLk2Rt8YwusYdrRYZUDmGxfnQMBeI
 5VShYN1wCGkC/AvtCtnB2EruA7hYQZ2gOo6W9yZco4vwX+EHJKRx8H5L6MY1yIp+
 O5ksYZbVOqNp0dTZSSZBYeNwHSjGhAlgwzlJ3gj4jSKBaCv2bwnasCq02MW36VZA
 3tOoIDtSPXyB9n2AVxcwycce7t5fa3SRtbto8CU+IvrYi6AR0GQzoKp4pTI7H4Sw
 8bF4w5Ah9qC1on8/Ivfv
 =ilkX
 -----END PGP SIGNATURE-----

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: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-01-31 12:53:21 +00:00
commit aefcd28366
10 changed files with 155 additions and 90 deletions

View file

@ -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);

View file

@ -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"

View file

@ -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);

View file

@ -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;
}
bytes_left -= ret;
buf += ret;
if (lseek(fd, offset, SEEK_SET) < 0) {
goto done;
}
return size - bytes_left;
ret = qemu_write_full(fd, buf, size);
done:
return ret;
}
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,49 +1665,57 @@ 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->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);
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;
}
rc = write_retry(d->fd, d->data, d->offset);
if (rc != d->offset) {
usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
0, 0, 0, 0);
goto done;
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;
}
/* Only for < 4G file sizes */
if (s->dataset.size != 0xFFFFFFFF && rc != s->dataset.size) {
usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans,
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 (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;
}
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);
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;
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:

View file

@ -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/>.
*/

View file

@ -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/>.
*/

View file

@ -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 <http://www.gnu.org/licenses/>.
*/

View file

@ -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/>.
*/

View file

@ -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) {
@ -1944,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);
@ -2023,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;
}
@ -2122,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);
@ -2295,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]);

View file

@ -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];