usb: usb3 streams support for usb-host and usb-redir
usb: xhci and mtp bugfixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJTgv7ZAAoJEEy22O7T6HE4FPgP/3eTqypKijaSwPAN7s0OqpKo DuI9bXPRlludn1zWGi/Da7iYDy0OwlfDksJddU3I8Qdxo+Y5DG/ERh0yxaKoE8Cw bT/eAtTmtAuCI0Spu0TdojPUDmjCIpAoO1Ta3WTi0IqR8LhMUyWwyMeQafMujeCn QvXax+Xw5wl7a6ogk1mBa8IfHBtmQIcscT7oc9pg59/zlcN7omW/g5pkz8pLjzmu LQYiSkUGt6LEViaKG2rTZ/dEOzqLe5Ot8SZ4Y2r+C6yOjsdG9J1kOhU5wGs1WuVU i57Zj/VRlZ3jLxCeU3dBT+kgJvi3mCnPZFni67iBtAlDDreSchVJJwY42SNZxea0 yLZSMFyMF+LBNrlbA9RFqlrnzr1sSfcjaOhQOajP6IVfjCsAiv0zivUJ4u6ijZ4R cGk1oJYsN888LUzgaszA3MTy593e7MI1cpPL79gt3lhh/Fu+mqTkFxcEUvrHqwSi 0LepENhE3tIgqSZH7MEtos+Avf8Uo2x/RULYZV3D0qRLLkhGcafJYIhb6mQsJug3 8D45yQ8YnsAmxpah0WopshlNxOi2hc/e5ADOCh4H+fNTmX5J9oN2IM4bQqqE+ci4 IkcYyyruNW5RGgHjlOZfAceC/CJ8Er/ocSuwvF9uTEiZ+unGotD4Xhaxyfrr3B+G BHkkzcySLzGfkbUuebSL =y+dq -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/pull-usb-7' into staging usb: usb3 streams support for usb-host and usb-redir usb: xhci and mtp bugfixes. # gpg: Signature made Mon 26 May 2014 09:44:09 BST using RSA key ID D3E87138 # gpg: Can't check signature: public key not found * remotes/kraxel/tags/pull-usb-7: usb-host-libusb: Set stream id when submitting bulk-stream transfers usb-host-libusb: Add alloc / free streams ops usb-host-libusb: Fill in endpoint max_streams when available usb-redir: Add support for bulk streams usb-mtp: handle usb_mtp_get_object failure usb-mtp: handle lseek failure usb-mtp: use bool to track MTPObject init status xhci: add xhci_get_flag xhci: add endpoint cap on express bus only xhci: child detach fix Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
972b09c219
@ -46,6 +46,7 @@ enum mtp_code {
|
||||
|
||||
/* response codes */
|
||||
RES_OK = 0x2001,
|
||||
RES_GENERAL_ERROR = 0x2002,
|
||||
RES_SESSION_NOT_OPEN = 0x2003,
|
||||
RES_INVALID_TRANSACTION_ID = 0x2004,
|
||||
RES_OPERATION_NOT_SUPPORTED = 0x2005,
|
||||
@ -109,7 +110,8 @@ struct MTPObject {
|
||||
struct stat stat;
|
||||
MTPObject *parent;
|
||||
MTPObject **children;
|
||||
int32_t nchildren;
|
||||
uint32_t nchildren;
|
||||
bool have_children;
|
||||
QTAILQ_ENTRY(MTPObject) next;
|
||||
};
|
||||
|
||||
@ -273,7 +275,6 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
|
||||
o->handle = handle;
|
||||
o->parent = parent;
|
||||
o->name = g_strdup(name);
|
||||
o->nchildren = -1;
|
||||
if (parent == NULL) {
|
||||
o->path = g_strdup(name);
|
||||
} else {
|
||||
@ -340,7 +341,11 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
||||
struct dirent *entry;
|
||||
DIR *dir;
|
||||
|
||||
o->nchildren = 0;
|
||||
if (o->have_children) {
|
||||
return;
|
||||
}
|
||||
o->have_children = true;
|
||||
|
||||
dir = opendir(o->path);
|
||||
if (!dir) {
|
||||
return;
|
||||
@ -698,7 +703,10 @@ static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c,
|
||||
if (offset > o->stat.st_size) {
|
||||
offset = o->stat.st_size;
|
||||
}
|
||||
lseek(d->fd, offset, SEEK_SET);
|
||||
if (lseek(d->fd, offset, SEEK_SET) < 0) {
|
||||
usb_mtp_data_free(d);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d->length = c->argv[2];
|
||||
if (d->length > o->stat.st_size - offset) {
|
||||
@ -789,9 +797,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
||||
c->trans, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (o->nchildren == -1) {
|
||||
usb_mtp_object_readdir(s, o);
|
||||
}
|
||||
usb_mtp_object_readdir(s, o);
|
||||
if (c->code == CMD_GET_NUM_OBJECTS) {
|
||||
trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path);
|
||||
nres = 1;
|
||||
@ -823,7 +829,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
||||
}
|
||||
data_in = usb_mtp_get_object(s, c, o);
|
||||
if (NULL == data_in) {
|
||||
fprintf(stderr, "%s: TODO: handle error\n", __func__);
|
||||
usb_mtp_queue_result(s, RES_GENERAL_ERROR,
|
||||
c->trans, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CMD_GET_PARTIAL_OBJECT:
|
||||
@ -840,7 +848,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
||||
}
|
||||
data_in = usb_mtp_get_partial_object(s, c, o);
|
||||
if (NULL == data_in) {
|
||||
fprintf(stderr, "%s: TODO: handle error\n", __func__);
|
||||
usb_mtp_queue_result(s, RES_GENERAL_ERROR,
|
||||
c->trans, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
nres = 1;
|
||||
res0 = data_in->length;
|
||||
|
@ -621,6 +621,11 @@ static const char *ep_state_name(uint32_t state)
|
||||
ARRAY_SIZE(ep_state_names));
|
||||
}
|
||||
|
||||
static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit)
|
||||
{
|
||||
return xhci->flags & (1 << bit);
|
||||
}
|
||||
|
||||
static uint64_t xhci_mfindex_get(XHCIState *xhci)
|
||||
{
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
@ -3435,7 +3440,7 @@ static void xhci_child_detach(USBPort *uport, USBDevice *child)
|
||||
USBBus *bus = usb_bus_from_device(child);
|
||||
XHCIState *xhci = container_of(bus, XHCIState, bus);
|
||||
|
||||
xhci_detach_slot(xhci, uport);
|
||||
xhci_detach_slot(xhci, child->port);
|
||||
}
|
||||
|
||||
static USBPortOps xhci_uport_ops = {
|
||||
@ -3594,13 +3599,15 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
&xhci->mem);
|
||||
|
||||
ret = pcie_endpoint_cap_init(dev, 0xa0);
|
||||
assert(ret >= 0);
|
||||
if (pci_bus_is_express(dev->bus)) {
|
||||
ret = pcie_endpoint_cap_init(dev, 0xa0);
|
||||
assert(ret >= 0);
|
||||
}
|
||||
|
||||
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
|
||||
if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) {
|
||||
msi_init(dev, 0x70, xhci->numintrs, true, false);
|
||||
}
|
||||
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
|
||||
if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) {
|
||||
msix_init(dev, xhci->numintrs,
|
||||
&xhci->mem, 0, OFF_MSIX_TABLE,
|
||||
&xhci->mem, 0, OFF_MSIX_PBA,
|
||||
|
@ -720,6 +720,9 @@ static void usb_host_ep_update(USBHostDevice *s)
|
||||
struct libusb_config_descriptor *conf;
|
||||
const struct libusb_interface_descriptor *intf;
|
||||
const struct libusb_endpoint_descriptor *endp;
|
||||
#if LIBUSBX_API_VERSION >= 0x01000103
|
||||
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
|
||||
#endif
|
||||
uint8_t devep, type;
|
||||
int pid, ep;
|
||||
int rc, i, e;
|
||||
@ -765,6 +768,15 @@ static void usb_host_ep_update(USBHostDevice *s)
|
||||
usb_ep_set_type(udev, pid, ep, type);
|
||||
usb_ep_set_ifnum(udev, pid, ep, i);
|
||||
usb_ep_set_halted(udev, pid, ep, 0);
|
||||
#if LIBUSBX_API_VERSION >= 0x01000103
|
||||
if (type == LIBUSB_TRANSFER_TYPE_BULK &&
|
||||
libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
|
||||
&endp_ss_comp) == LIBUSB_SUCCESS) {
|
||||
usb_ep_set_max_streams(udev, pid, ep,
|
||||
endp_ss_comp->bmAttributes);
|
||||
libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1202,10 +1214,23 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
|
||||
usb_packet_copy(p, r->buffer, size);
|
||||
}
|
||||
ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
|
||||
libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
|
||||
r->buffer, size,
|
||||
usb_host_req_complete_data, r,
|
||||
BULK_TIMEOUT);
|
||||
if (p->stream) {
|
||||
#if LIBUSBX_API_VERSION >= 0x01000103
|
||||
libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
|
||||
r->buffer, size,
|
||||
usb_host_req_complete_data, r,
|
||||
BULK_TIMEOUT);
|
||||
#else
|
||||
usb_host_req_free(r);
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
|
||||
r->buffer, size,
|
||||
usb_host_req_complete_data, r,
|
||||
BULK_TIMEOUT);
|
||||
}
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
|
||||
@ -1268,6 +1293,54 @@ static void usb_host_handle_reset(USBDevice *udev)
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
|
||||
int nr_eps, int streams)
|
||||
{
|
||||
#if LIBUSBX_API_VERSION >= 0x01000103
|
||||
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
||||
unsigned char endpoints[30];
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < nr_eps; i++) {
|
||||
endpoints[i] = eps[i]->nr;
|
||||
if (eps[i]->pid == USB_TOKEN_IN) {
|
||||
endpoints[i] |= 0x80;
|
||||
}
|
||||
}
|
||||
rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps);
|
||||
if (rc < 0) {
|
||||
usb_host_libusb_error("libusb_alloc_streams", rc);
|
||||
} else if (rc != streams) {
|
||||
fprintf(stderr,
|
||||
"libusb_alloc_streams: got less streams then requested %d < %d\n",
|
||||
rc, streams);
|
||||
}
|
||||
|
||||
return (rc == streams) ? 0 : -1;
|
||||
#else
|
||||
fprintf(stderr, "libusb_alloc_streams: error not implemented\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
|
||||
int nr_eps)
|
||||
{
|
||||
#if LIBUSBX_API_VERSION >= 0x01000103
|
||||
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
||||
unsigned char endpoints[30];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_eps; i++) {
|
||||
endpoints[i] = eps[i]->nr;
|
||||
if (eps[i]->pid == USB_TOKEN_IN) {
|
||||
endpoints[i] |= 0x80;
|
||||
}
|
||||
}
|
||||
libusb_free_streams(s->dh, endpoints, nr_eps);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* This is *NOT* about restoring state. We have absolutely no idea
|
||||
* what state the host device is in at the moment and whenever it is
|
||||
@ -1349,6 +1422,8 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
|
||||
uc->handle_reset = usb_host_handle_reset;
|
||||
uc->handle_destroy = usb_host_handle_destroy;
|
||||
uc->flush_ep_queue = usb_host_flush_ep_queue;
|
||||
uc->alloc_streams = usb_host_alloc_streams;
|
||||
uc->free_streams = usb_host_free_streams;
|
||||
dc->vmsd = &vmstate_usb_host;
|
||||
dc->props = usb_host_dev_properties;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
|
@ -50,6 +50,10 @@
|
||||
((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \
|
||||
(i) & 0x0f))
|
||||
|
||||
#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */
|
||||
#define USBREDIR_VERSION 0
|
||||
#endif
|
||||
|
||||
typedef struct USBRedirDevice USBRedirDevice;
|
||||
|
||||
/* Struct to hold buffered packets */
|
||||
@ -68,6 +72,7 @@ struct endp_data {
|
||||
uint8_t interval;
|
||||
uint8_t interface; /* bInterfaceNumber this ep belongs to */
|
||||
uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
|
||||
uint32_t max_streams;
|
||||
uint8_t iso_started;
|
||||
uint8_t iso_error; /* For reporting iso errors to the HC */
|
||||
uint8_t interrupt_started;
|
||||
@ -106,8 +111,9 @@ struct USBRedirDevice {
|
||||
int read_buf_size;
|
||||
/* Active chardev-watch-tag */
|
||||
guint watch;
|
||||
/* For async handling of close */
|
||||
/* For async handling of close / reject */
|
||||
QEMUBH *chardev_close_bh;
|
||||
QEMUBH *device_reject_bh;
|
||||
/* To delay the usb attach in case of quick chardev close + open */
|
||||
QEMUTimer *attach_timer;
|
||||
int64_t next_attach_time;
|
||||
@ -780,11 +786,12 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0;
|
||||
}
|
||||
|
||||
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
|
||||
DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n",
|
||||
ep, p->stream, size, p->id);
|
||||
|
||||
bulk_packet.endpoint = ep;
|
||||
bulk_packet.length = size;
|
||||
bulk_packet.stream_id = 0;
|
||||
bulk_packet.stream_id = p->stream;
|
||||
bulk_packet.length_high = size >> 16;
|
||||
assert(bulk_packet.length_high == 0 ||
|
||||
usbredirparser_peer_has_cap(dev->parser,
|
||||
@ -1091,6 +1098,66 @@ static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps,
|
||||
int nr_eps, int streams)
|
||||
{
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
#if USBREDIR_VERSION >= 0x000700
|
||||
struct usb_redir_alloc_bulk_streams_header alloc_streams;
|
||||
int i;
|
||||
|
||||
if (!usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_bulk_streams)) {
|
||||
ERROR("peer does not support streams\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (streams == 0) {
|
||||
ERROR("request to allocate 0 streams\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
alloc_streams.no_streams = streams;
|
||||
alloc_streams.endpoints = 0;
|
||||
for (i = 0; i < nr_eps; i++) {
|
||||
alloc_streams.endpoints |= 1 << USBEP2I(eps[i]);
|
||||
}
|
||||
usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
ERROR("usbredir_alloc_streams not implemented\n");
|
||||
goto reject;
|
||||
#endif
|
||||
reject:
|
||||
ERROR("streams are not available, disconnecting\n");
|
||||
qemu_bh_schedule(dev->device_reject_bh);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps,
|
||||
int nr_eps)
|
||||
{
|
||||
#if USBREDIR_VERSION >= 0x000700
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
struct usb_redir_free_bulk_streams_header free_streams;
|
||||
int i;
|
||||
|
||||
if (!usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_bulk_streams)) {
|
||||
return;
|
||||
}
|
||||
|
||||
free_streams.endpoints = 0;
|
||||
for (i = 0; i < nr_eps; i++) {
|
||||
free_streams.endpoints |= 1 << USBEP2I(eps[i]);
|
||||
}
|
||||
usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Close events can be triggered by usbredirparser_do_write which gets called
|
||||
* from within the USBDevice data / control packet callbacks and doing a
|
||||
@ -1102,6 +1169,7 @@ static void usbredir_chardev_close_bh(void *opaque)
|
||||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
|
||||
qemu_bh_cancel(dev->device_reject_bh);
|
||||
usbredir_device_disconnect(dev);
|
||||
|
||||
if (dev->parser) {
|
||||
@ -1153,6 +1221,9 @@ static void usbredir_create_parser(USBRedirDevice *dev)
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
|
||||
#if USBREDIR_VERSION >= 0x000700
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
|
||||
#endif
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
flags |= usbredirparser_fl_no_hello;
|
||||
@ -1171,6 +1242,17 @@ static void usbredir_reject_device(USBRedirDevice *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We may need to reject the device when the hcd calls alloc_streams, doing
|
||||
* an usb_detach from within a hcd call is not a good idea, hence this bh.
|
||||
*/
|
||||
static void usbredir_device_reject_bh(void *opaque)
|
||||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
|
||||
usbredir_reject_device(dev);
|
||||
}
|
||||
|
||||
static void usbredir_do_attach(void *opaque)
|
||||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
@ -1297,6 +1379,7 @@ static int usbredir_initfn(USBDevice *udev)
|
||||
}
|
||||
|
||||
dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
|
||||
dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev);
|
||||
dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev);
|
||||
|
||||
packet_id_queue_init(&dev->cancelled, dev, "cancelled");
|
||||
@ -1337,6 +1420,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
|
||||
dev->cs = NULL;
|
||||
/* Note must be done after qemu_chr_close, as that causes a close event */
|
||||
qemu_bh_delete(dev->chardev_close_bh);
|
||||
qemu_bh_delete(dev->device_reject_bh);
|
||||
|
||||
timer_del(dev->attach_timer);
|
||||
timer_free(dev->attach_timer);
|
||||
@ -1628,6 +1712,7 @@ static void usbredir_setup_usb_eps(USBRedirDevice *dev)
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
||||
usb_ep->max_streams = dev->endpoint[i].max_streams;
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
}
|
||||
@ -1646,6 +1731,12 @@ static void usbredir_ep_info(void *priv,
|
||||
usb_redir_cap_ep_info_max_packet_size)) {
|
||||
dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
|
||||
}
|
||||
#if USBREDIR_VERSION >= 0x000700
|
||||
if (usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_bulk_streams)) {
|
||||
dev->endpoint[i].max_streams = ep_info->max_streams[i];
|
||||
}
|
||||
#endif
|
||||
switch (dev->endpoint[i].type) {
|
||||
case usb_redir_type_invalid:
|
||||
break;
|
||||
@ -1779,6 +1870,20 @@ static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
|
||||
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
|
||||
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
|
||||
{
|
||||
#if USBREDIR_VERSION >= 0x000700
|
||||
USBRedirDevice *dev = priv;
|
||||
|
||||
if (bulk_streams_status->status == usb_redir_success) {
|
||||
DPRINTF("bulk streams status %d eps %08x\n",
|
||||
bulk_streams_status->status, bulk_streams_status->endpoints);
|
||||
} else {
|
||||
ERROR("bulk streams %s failed status %d eps %08x\n",
|
||||
(bulk_streams_status->no_streams == 0) ? "free" : "alloc",
|
||||
bulk_streams_status->status, bulk_streams_status->endpoints);
|
||||
ERROR("usb-redir-host does not provide streams, disconnecting\n");
|
||||
usbredir_reject_device(dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void usbredir_bulk_receiving_status(void *priv, uint64_t id,
|
||||
@ -1850,8 +1955,8 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
int len = (bulk_packet->length_high << 16) | bulk_packet->length;
|
||||
USBPacket *p;
|
||||
|
||||
DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
|
||||
bulk_packet->status, ep, len, id);
|
||||
DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n",
|
||||
bulk_packet->status, ep, bulk_packet->stream_id, len, id);
|
||||
|
||||
p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
@ -2165,6 +2270,23 @@ static bool usbredir_bulk_receiving_needed(void *priv)
|
||||
return endp->bulk_receiving_started;
|
||||
}
|
||||
|
||||
static const VMStateDescription usbredir_stream_vmstate = {
|
||||
.name = "usb-redir-ep/stream-state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(max_streams, struct endp_data),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static bool usbredir_stream_needed(void *priv)
|
||||
{
|
||||
struct endp_data *endp = priv;
|
||||
|
||||
return endp->max_streams;
|
||||
}
|
||||
|
||||
static const VMStateDescription usbredir_ep_vmstate = {
|
||||
.name = "usb-redir-ep",
|
||||
.version_id = 1,
|
||||
@ -2196,6 +2318,9 @@ static const VMStateDescription usbredir_ep_vmstate = {
|
||||
{
|
||||
.vmsd = &usbredir_bulk_receiving_vmstate,
|
||||
.needed = usbredir_bulk_receiving_needed,
|
||||
}, {
|
||||
.vmsd = &usbredir_stream_vmstate,
|
||||
.needed = usbredir_stream_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
@ -2361,6 +2486,8 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
|
||||
uc->handle_control = usbredir_handle_control;
|
||||
uc->flush_ep_queue = usbredir_flush_ep_queue;
|
||||
uc->ep_stopped = usbredir_ep_stopped;
|
||||
uc->alloc_streams = usbredir_alloc_streams;
|
||||
uc->free_streams = usbredir_free_streams;
|
||||
dc->vmsd = &usbredir_vmstate;
|
||||
dc->props = usbredir_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
|
Loading…
Reference in New Issue
Block a user