Compare commits
	
		
			10 Commits
		
	
	
		
			pull-conso
			...
			pull-usb-7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8d1bd3c901 | ||
|  | 56a9f18051 | ||
|  | b664b80f19 | ||
|  | 19e8393170 | ||
|  | 22513a9b44 | ||
|  | 68206d7342 | ||
|  | 36084d7e31 | ||
|  | f995523582 | ||
|  | 058fdcf52c | ||
|  | 463c534db5 | 
| @@ -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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user