*/
#include "qemu/osdep.h"
+#include "qemu-common.h"
#include "qemu/units.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "qemu/timer.h"
+#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "qemu/iov.h"
+#include "qemu/module.h"
#include "chardev/char-fe.h"
#include <usbredirparser.h>
#include <usbredirfilter.h>
+#include "hw/qdev-properties.h"
#include "hw/usb.h"
+#include "migration/qemu-file-types.h"
+#include "migration/vmstate.h"
/* ERROR is defined below. Remove any previous definition. */
#undef ERROR
/* Properties */
CharBackend cs;
bool enable_streams;
+ bool suppress_remote_wake;
+ bool in_write;
uint8_t debug;
int32_t bootindex;
char *filter_str;
return 0;
}
+ /* Recursion check */
+ if (dev->in_write) {
+ DPRINTF("usbredir_write recursion\n");
+ return 0;
+ }
+ dev->in_write = true;
+
r = qemu_chr_fe_write(&dev->cs, data, count);
if (r < count) {
if (!dev->watch) {
r = 0;
}
}
+ dev->in_write = false;
return r;
}
USBPacket *p, uint8_t ep)
{
/* Input interrupt endpoint, buffered packet input */
- struct buf_packet *intp;
- int status, len;
+ struct buf_packet *intp, *intp_to_free;
+ int status, len, sum;
if (!dev->endpoint[EP2I(ep)].interrupt_started &&
!dev->endpoint[EP2I(ep)].interrupt_error) {
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
- intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+ /* check for completed interrupt message (with all fragments) */
+ sum = 0;
+ QTAILQ_FOREACH(intp, &dev->endpoint[EP2I(ep)].bufpq, next) {
+ sum += intp->len;
+ if (intp->len < dev->endpoint[EP2I(ep)].max_packet_size ||
+ sum >= p->iov.size)
+ break;
+ }
+
if (intp == NULL) {
- DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
+ DPRINTF2("interrupt-token-in ep %02X, no intp, buffered %d\n", ep, sum);
/* Check interrupt_error for stream errors */
status = dev->endpoint[EP2I(ep)].interrupt_error;
dev->endpoint[EP2I(ep)].interrupt_error = 0;
}
return;
}
- DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
- intp->status, intp->len);
- status = intp->status;
- len = intp->len;
- if (len > p->iov.size) {
- ERROR("received int data is larger then packet ep %02X\n", ep);
- len = p->iov.size;
- status = usb_redir_babble;
+ /* copy of completed interrupt message */
+ sum = 0;
+ status = usb_redir_success;
+ intp_to_free = NULL;
+ QTAILQ_FOREACH(intp, &dev->endpoint[EP2I(ep)].bufpq, next) {
+ if (intp_to_free) {
+ bufp_free(dev, intp_to_free, ep);
+ }
+ DPRINTF("interrupt-token-in ep %02X fragment status %d len %d\n", ep,
+ intp->status, intp->len);
+
+ sum += intp->len;
+ len = intp->len;
+ if (status == usb_redir_success) {
+ status = intp->status;
+ }
+ if (sum > p->iov.size) {
+ ERROR("received int data is larger then packet ep %02X\n", ep);
+ len -= (sum - p->iov.size);
+ sum = p->iov.size;
+ status = usb_redir_babble;
+ }
+
+ usb_packet_copy(p, intp->data, len);
+
+ intp_to_free = intp;
+ if (intp->len < dev->endpoint[EP2I(ep)].max_packet_size ||
+ sum >= p->iov.size)
+ break;
}
- usb_packet_copy(p, intp->data, len);
- bufp_free(dev, intp, ep);
+ if (intp_to_free) {
+ bufp_free(dev, intp_to_free, ep);
+ }
+ DPRINTF("interrupt-token-in ep %02X summary status %d len %d\n", ep,
+ status, sum);
usbredir_handle_status(dev, p, status);
}
usbredirparser_do_write(dev->parser);
}
-static void usbredir_chardev_event(void *opaque, int event)
+static void usbredir_chardev_event(void *opaque, QEMUChrEvent event)
{
USBRedirDevice *dev = opaque;
DPRINTF("chardev close\n");
qemu_bh_schedule(dev->chardev_close_bh);
break;
+ case CHR_EVENT_BREAK:
+ case CHR_EVENT_MUX_IN:
+ case CHR_EVENT_MUX_OUT:
+ /* Ignore */
+ break;
}
}
}
}
-static void usbredir_unrealize(USBDevice *udev, Error **errp)
+static void usbredir_unrealize(USBDevice *udev)
{
USBRedirDevice *dev = USB_REDIRECT(udev);
for (i = EP2I(USB_DIR_IN); i < MAX_ENDPOINTS; i++) {
dev->endpoint[i].bulk_receiving_enabled = 0;
}
+
+ if (dev->interface_info.interface_count == NO_INTERFACE_INFO) {
+ return;
+ }
+
for (i = 0; i < dev->interface_info.interface_count; i++) {
quirks = usb_get_quirks(dev->device_info.vendor_id,
dev->device_info.product_id,
USBRedirDevice *dev = priv;
int i;
+ assert(dev != NULL);
for (i = 0; i < MAX_ENDPOINTS; i++) {
dev->endpoint[i].type = ep_info->type[i];
dev->endpoint[i].interval = ep_info->interval[i];
memcpy(dev->dev.data_buf, data, data_len);
}
p->actual_length = len;
+ /*
+ * If this is GET_DESCRIPTOR request for configuration descriptor,
+ * remove 'remote wakeup' flag from it to prevent idle power down
+ * in Windows guest
+ */
+ if (dev->suppress_remote_wake &&
+ control_packet->requesttype == USB_DIR_IN &&
+ control_packet->request == USB_REQ_GET_DESCRIPTOR &&
+ control_packet->value == (USB_DT_CONFIG << 8) &&
+ control_packet->index == 0 &&
+ /* bmAttributes field of config descriptor */
+ len > 7 && (dev->dev.data_buf[7] & USB_CFG_ATT_WAKEUP)) {
+ DPRINTF("Removed remote wake %04X:%04X\n",
+ dev->device_info.vendor_id,
+ dev->device_info.product_id);
+ dev->dev.data_buf[7] &= ~USB_CFG_ATT_WAKEUP;
+ }
usb_generic_async_ctrl_complete(&dev->dev, p);
}
free(data);
}
if (ep & USB_DIR_IN) {
- bool q_was_empty;
-
if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
DPRINTF("received int packet while not started ep %02X\n", ep);
free(data);
return;
}
- q_was_empty = QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq);
-
/* bufp_alloc also adds the packet to the ep queue */
bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data);
- if (q_was_empty) {
- usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
- }
+ /* insufficient data solved with USB_RET_NAK */
+ usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
} else {
/*
* We report output interrupt packets as completed directly upon
{
USBRedirDevice *dev = priv;
- if (dev->parser == NULL) {
+ if (dev == NULL || dev->parser == NULL) {
return 0;
}
/* For usbredirparser migration */
static int usbredir_put_parser(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field, QJSON *vmdesc)
+ const VMStateField *field, QJSON *vmdesc)
{
USBRedirDevice *dev = priv;
uint8_t *data;
}
static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field)
+ const VMStateField *field)
{
USBRedirDevice *dev = priv;
uint8_t *data;
/* For buffered packets (iso/irq) queue migration */
static int usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field, QJSON *vmdesc)
+ const VMStateField *field, QJSON *vmdesc)
{
struct endp_data *endp = priv;
USBRedirDevice *dev = endp->dev;
}
static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field)
+ const VMStateField *field)
{
struct endp_data *endp = priv;
USBRedirDevice *dev = endp->dev;
/* For PacketIdQueue migration */
static int usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field, QJSON *vmdesc)
+ const VMStateField *field, QJSON *vmdesc)
{
struct PacketIdQueue *q = priv;
USBRedirDevice *dev = q->dev;
}
static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused,
- VMStateField *field)
+ const VMStateField *field)
{
struct PacketIdQueue *q = priv;
USBRedirDevice *dev = q->dev;
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true),
+ DEFINE_PROP_BOOL("suppress-remote-wake", USBRedirDevice,
+ suppress_remote_wake, true),
DEFINE_PROP_END_OF_LIST(),
};
uc->alloc_streams = usbredir_alloc_streams;
uc->free_streams = usbredir_free_streams;
dc->vmsd = &usbredir_vmstate;
- dc->props = usbredir_properties;
+ device_class_set_props(dc, usbredir_properties);
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
device_add_bootindex_property(obj, &dev->bootindex,
"bootindex", NULL,
- &udev->qdev, NULL);
+ &udev->qdev);
}
static const TypeInfo usbredir_dev_info = {