mirror of https://github.com/xqemu/xqemu.git
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:
commit
aefcd28366
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
172
hw/usb/dev-mtp.c
172
hw/usb/dev-mtp.c
|
@ -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:
|
||||
|
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue