mirror of https://github.com/xemu-project/xemu.git
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:
commit
78ee443e52
|
@ -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:
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue