usb: mtp write support, bugfixes.

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJbe9dIAAoJEEy22O7T6HE46VEQAKFV2hHj4V6NzlHoYaGdNx1E
 50thQEnLkBEdEfWLtkqTQRKxG24IFUSLLrYwjQlrQp+mIfisfqeM7TEsYH/Jaij/
 p8VwHj4QaaWqjvttpMMMzqx0YbIX0HDWy9/xRwVrDQ+rUxt9DHUzCH1bWuHc35eV
 sjslk9m0Lt49nlC24OKFwPwy9v7WpuEb3D86cRePjwrRQzEGxTTPhftziDrUZBBs
 jhiHLjZOlKGaK7yt5fkpS5MlY/4rYVVRlFaPoEbAjOiFxNbDQBpBGKNfyKhvwWg0
 b5C3LnfbNeEdowVTtYdkx1IC93e2gO4d+jtsT153FafXirr7+dpB9oFzSS67gCIB
 9AkYnewhmM/ZzBTJV21VSMN62gNJT3rt90K/C2R/qMGNxBJZGhmUeUGIsUPPp1KT
 2XOkovwlq/+UZdTwAgP3liQekT7EtS4BH8mQaDTRuRLytst0Gx+b6Vgz2sF6NzI0
 kn4kgiirFe8Z8oZFZEa049cpM1McwXN/Wej/vdHAd132enqaf5O7pgEmKQDe4Esm
 fv1jf7jxm822e2OvOkIHF34vugw1YN3z1/f2J/9Eq2j1/tttPlG9fmmrphiGvtDr
 gfDk0fhhCKSC8j+MC6OxEguTZz8j7qJyHtHi81XtW4NkJVuz7p3CgZK04jWeRKYo
 ePsFfWufncbHTyh5iLD/
 =3TQ3
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2018-08-21 18:00:27 +01:00
commit 78ee443e52
3 changed files with 84 additions and 16 deletions

View File

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

View File

@ -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(),

View File

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