2011-07-19 11:04:10 +02:00
|
|
|
/*
|
|
|
|
* USB redirector usb-guest
|
|
|
|
*
|
2012-08-27 16:33:08 +02:00
|
|
|
* Copyright (c) 2011-2012 Red Hat, Inc.
|
2011-07-19 11:04:10 +02:00
|
|
|
*
|
|
|
|
* Red Hat Authors:
|
|
|
|
* Hans de Goede <hdegoede@redhat.com>
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qemu-common.h"
|
2012-12-17 18:20:00 +01:00
|
|
|
#include "qemu/timer.h"
|
2012-12-17 18:20:04 +01:00
|
|
|
#include "sysemu/sysemu.h"
|
2015-03-17 17:22:46 +01:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2015-03-17 18:29:20 +01:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-17 18:20:00 +01:00
|
|
|
#include "qemu/iov.h"
|
2013-04-08 16:55:25 +02:00
|
|
|
#include "sysemu/char.h"
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <usbredirparser.h>
|
2012-01-25 09:50:39 +01:00
|
|
|
#include <usbredirfilter.h>
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
#include "hw/usb.h"
|
|
|
|
|
|
|
|
#define MAX_ENDPOINTS 32
|
2012-03-31 17:14:21 +02:00
|
|
|
#define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */
|
2011-07-19 11:04:10 +02:00
|
|
|
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
|
|
|
|
#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
|
2012-12-14 14:35:42 +01:00
|
|
|
#define USBEP2I(usb_ep) (((usb_ep)->pid == USB_TOKEN_IN) ? \
|
|
|
|
((usb_ep)->nr | 0x10) : ((usb_ep)->nr))
|
|
|
|
#define I2USBEP(d, i) (usb_ep_get(&(d)->dev, \
|
|
|
|
((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \
|
|
|
|
(i) & 0x0f))
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */
|
|
|
|
#define USBREDIR_VERSION 0
|
|
|
|
#endif
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
typedef struct USBRedirDevice USBRedirDevice;
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
/* Struct to hold buffered packets */
|
2011-07-19 11:04:10 +02:00
|
|
|
struct buf_packet {
|
|
|
|
uint8_t *data;
|
2012-12-19 15:08:33 +01:00
|
|
|
void *free_on_destroy;
|
|
|
|
uint16_t len;
|
|
|
|
uint16_t offset;
|
|
|
|
uint8_t status;
|
2011-07-19 11:04:10 +02:00
|
|
|
QTAILQ_ENTRY(buf_packet)next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct endp_data {
|
2012-12-14 14:35:46 +01:00
|
|
|
USBRedirDevice *dev;
|
2011-07-19 11:04:10 +02:00
|
|
|
uint8_t type;
|
|
|
|
uint8_t interval;
|
|
|
|
uint8_t interface; /* bInterfaceNumber this ep belongs to */
|
2012-09-12 15:08:36 +02:00
|
|
|
uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
|
2013-11-19 14:37:03 +01:00
|
|
|
uint32_t max_streams;
|
2011-07-19 11:04:10 +02:00
|
|
|
uint8_t iso_started;
|
|
|
|
uint8_t iso_error; /* For reporting iso errors to the HC */
|
|
|
|
uint8_t interrupt_started;
|
|
|
|
uint8_t interrupt_error;
|
2012-12-19 15:08:33 +01:00
|
|
|
uint8_t bulk_receiving_enabled;
|
|
|
|
uint8_t bulk_receiving_started;
|
2012-01-10 14:13:06 +01:00
|
|
|
uint8_t bufpq_prefilled;
|
2012-01-10 14:13:07 +01:00
|
|
|
uint8_t bufpq_dropping_packets;
|
2011-07-19 11:04:10 +02:00
|
|
|
QTAILQ_HEAD(, buf_packet) bufpq;
|
2012-09-12 15:08:37 +02:00
|
|
|
int32_t bufpq_size;
|
|
|
|
int32_t bufpq_target_size;
|
2012-12-19 15:08:33 +01:00
|
|
|
USBPacket *pending_async_packet;
|
2011-07-19 11:04:10 +02:00
|
|
|
};
|
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
struct PacketIdQueueEntry {
|
|
|
|
uint64_t id;
|
|
|
|
QTAILQ_ENTRY(PacketIdQueueEntry)next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PacketIdQueue {
|
|
|
|
USBRedirDevice *dev;
|
|
|
|
const char *name;
|
|
|
|
QTAILQ_HEAD(, PacketIdQueueEntry) head;
|
|
|
|
int size;
|
|
|
|
};
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
struct USBRedirDevice {
|
|
|
|
USBDevice dev;
|
|
|
|
/* Properties */
|
|
|
|
CharDriverState *cs;
|
|
|
|
uint8_t debug;
|
2012-01-25 09:50:39 +01:00
|
|
|
char *filter_str;
|
2012-03-22 10:48:03 +01:00
|
|
|
int32_t bootindex;
|
2011-07-19 11:04:10 +02:00
|
|
|
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
|
|
|
|
const uint8_t *read_buf;
|
|
|
|
int read_buf_size;
|
2013-03-19 14:54:38 +01:00
|
|
|
/* Active chardev-watch-tag */
|
|
|
|
guint watch;
|
2013-11-19 14:37:03 +01:00
|
|
|
/* For async handling of close / reject */
|
2012-08-23 16:37:19 +02:00
|
|
|
QEMUBH *chardev_close_bh;
|
2013-11-19 14:37:03 +01:00
|
|
|
QEMUBH *device_reject_bh;
|
2011-07-19 11:04:10 +02:00
|
|
|
/* To delay the usb attach in case of quick chardev close + open */
|
|
|
|
QEMUTimer *attach_timer;
|
|
|
|
int64_t next_attach_time;
|
|
|
|
struct usbredirparser *parser;
|
|
|
|
struct endp_data endpoint[MAX_ENDPOINTS];
|
2012-09-12 15:08:34 +02:00
|
|
|
struct PacketIdQueue cancelled;
|
2012-09-12 15:08:35 +02:00
|
|
|
struct PacketIdQueue already_in_flight;
|
2012-12-19 15:08:33 +01:00
|
|
|
void (*buffered_bulk_in_complete)(USBRedirDevice *, USBPacket *, uint8_t);
|
2012-01-25 09:50:39 +01:00
|
|
|
/* Data for device filtering */
|
|
|
|
struct usb_redir_device_connect_header device_info;
|
|
|
|
struct usb_redir_interface_info_header interface_info;
|
|
|
|
struct usbredirfilter_rule *filter_rules;
|
|
|
|
int filter_rules_count;
|
2012-10-31 13:47:15 +01:00
|
|
|
int compatible_speedmask;
|
2011-07-19 11:04:10 +02:00
|
|
|
};
|
|
|
|
|
2015-05-06 14:55:35 +02:00
|
|
|
#define TYPE_USB_REDIR "usb-redir"
|
|
|
|
#define USB_REDIRECT(obj) OBJECT_CHECK(USBRedirDevice, (obj), TYPE_USB_REDIR)
|
|
|
|
|
2012-02-26 16:14:45 +01:00
|
|
|
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
|
2011-07-19 11:04:10 +02:00
|
|
|
static void usbredir_device_connect(void *priv,
|
|
|
|
struct usb_redir_device_connect_header *device_connect);
|
|
|
|
static void usbredir_device_disconnect(void *priv);
|
|
|
|
static void usbredir_interface_info(void *priv,
|
|
|
|
struct usb_redir_interface_info_header *interface_info);
|
|
|
|
static void usbredir_ep_info(void *priv,
|
|
|
|
struct usb_redir_ep_info_header *ep_info);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_configuration_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_configuration_status_header *configuration_status);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_alt_setting_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_alt_setting_status_header *alt_setting_status);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_iso_stream_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_iso_stream_status_header *iso_stream_status);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_interrupt_receiving_status_header
|
|
|
|
*interrupt_receiving_status);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_bulk_streams_status_header *bulk_streams_status);
|
2012-12-19 15:08:33 +01:00
|
|
|
static void usbredir_bulk_receiving_status(void *priv, uint64_t id,
|
|
|
|
struct usb_redir_bulk_receiving_status_header *bulk_receiving_status);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_control_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_control_packet_header *control_packet,
|
|
|
|
uint8_t *data, int data_len);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_bulk_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_bulk_packet_header *bulk_packet,
|
|
|
|
uint8_t *data, int data_len);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_iso_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_iso_packet_header *iso_packet,
|
|
|
|
uint8_t *data, int data_len);
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_interrupt_packet_header *interrupt_header,
|
|
|
|
uint8_t *data, int data_len);
|
2012-12-19 15:08:33 +01:00
|
|
|
static void usbredir_buffered_bulk_packet(void *priv, uint64_t id,
|
|
|
|
struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet,
|
|
|
|
uint8_t *data, int data_len);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
|
|
|
|
int status);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-12 15:08:39 +02:00
|
|
|
#define VERSION "qemu usb-redir guest " QEMU_VERSION
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/*
|
|
|
|
* Logging stuff
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define ERROR(...) \
|
|
|
|
do { \
|
|
|
|
if (dev->debug >= usbredirparser_error) { \
|
|
|
|
error_report("usb-redir error: " __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define WARNING(...) \
|
|
|
|
do { \
|
|
|
|
if (dev->debug >= usbredirparser_warning) { \
|
|
|
|
error_report("usb-redir warning: " __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define INFO(...) \
|
|
|
|
do { \
|
|
|
|
if (dev->debug >= usbredirparser_info) { \
|
|
|
|
error_report("usb-redir: " __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define DPRINTF(...) \
|
|
|
|
do { \
|
|
|
|
if (dev->debug >= usbredirparser_debug) { \
|
|
|
|
error_report("usb-redir: " __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define DPRINTF2(...) \
|
|
|
|
do { \
|
|
|
|
if (dev->debug >= usbredirparser_debug_data) { \
|
|
|
|
error_report("usb-redir: " __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static void usbredir_log(void *priv, int level, const char *msg)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
if (dev->debug < level) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-20 18:13:08 +01:00
|
|
|
error_report("%s", msg);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
|
|
|
|
const uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
int i, j, n;
|
|
|
|
|
|
|
|
if (dev->debug < usbredirparser_debug_data) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < len; i += j) {
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
n = sprintf(buf, "%s", desc);
|
|
|
|
for (j = 0; j < 8 && i + j < len; j++) {
|
|
|
|
n += sprintf(buf + n, " %02X", data[i + j]);
|
|
|
|
}
|
2011-12-20 18:13:08 +01:00
|
|
|
error_report("%s", buf);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* usbredirparser io functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int usbredir_read(void *priv, uint8_t *data, int count)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
if (dev->read_buf_size < count) {
|
|
|
|
count = dev->read_buf_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, dev->read_buf, count);
|
|
|
|
|
|
|
|
dev->read_buf_size -= count;
|
|
|
|
if (dev->read_buf_size) {
|
|
|
|
dev->read_buf += count;
|
|
|
|
} else {
|
|
|
|
dev->read_buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2013-03-19 14:54:38 +01:00
|
|
|
static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
|
|
|
|
|
|
|
dev->watch = 0;
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
static int usbredir_write(void *priv, uint8_t *data, int count)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
2013-03-19 14:54:38 +01:00
|
|
|
int r;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2013-03-26 11:07:53 +01:00
|
|
|
if (!dev->cs->be_open) {
|
2011-11-19 10:22:47 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
/* Don't send new data to the chardev until our state is fully synced */
|
|
|
|
if (!runstate_check(RUN_STATE_RUNNING)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-19 14:54:38 +01:00
|
|
|
r = qemu_chr_fe_write(dev->cs, data, count);
|
|
|
|
if (r < count) {
|
|
|
|
if (!dev->watch) {
|
2014-05-23 17:57:49 +02:00
|
|
|
dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT|G_IO_HUP,
|
2013-03-19 14:54:38 +01:00
|
|
|
usbredir_write_unblocked, dev);
|
|
|
|
}
|
|
|
|
if (r < 0) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-08-28 11:30:13 +02:00
|
|
|
* Cancelled and buffered packets helpers
|
2011-07-19 11:04:10 +02:00
|
|
|
*/
|
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
static void packet_id_queue_init(struct PacketIdQueue *q,
|
|
|
|
USBRedirDevice *dev, const char *name)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-09-12 15:08:34 +02:00
|
|
|
q->dev = dev;
|
|
|
|
q->name = name;
|
|
|
|
QTAILQ_INIT(&q->head);
|
|
|
|
q->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = q->dev;
|
|
|
|
struct PacketIdQueueEntry *e;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
|
|
|
|
|
2015-10-29 16:55:22 +01:00
|
|
|
e = g_new0(struct PacketIdQueueEntry, 1);
|
2012-09-12 15:08:34 +02:00
|
|
|
e->id = id;
|
|
|
|
QTAILQ_INSERT_TAIL(&q->head, e, next);
|
|
|
|
q->size++;
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = q->dev;
|
|
|
|
struct PacketIdQueueEntry *e;
|
2012-08-28 11:30:13 +02:00
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
QTAILQ_FOREACH(e, &q->head, next) {
|
|
|
|
if (e->id == id) {
|
|
|
|
DPRINTF("removing packet id %"PRIu64" from %s queue\n",
|
|
|
|
id, q->name);
|
|
|
|
QTAILQ_REMOVE(&q->head, e, next);
|
|
|
|
q->size--;
|
|
|
|
g_free(e);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void packet_id_queue_empty(struct PacketIdQueue *q)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = q->dev;
|
|
|
|
struct PacketIdQueueEntry *e, *next_e;
|
|
|
|
|
|
|
|
DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name);
|
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) {
|
|
|
|
QTAILQ_REMOVE(&q->head, e, next);
|
|
|
|
g_free(e);
|
|
|
|
}
|
|
|
|
q->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2012-12-19 15:08:33 +01:00
|
|
|
int i = USBEP2I(p->ep);
|
2012-09-12 15:08:34 +02:00
|
|
|
|
2012-10-31 13:47:12 +01:00
|
|
|
if (p->combined) {
|
|
|
|
usb_combined_packet_cancel(udev, p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
if (dev->endpoint[i].pending_async_packet) {
|
|
|
|
assert(dev->endpoint[i].pending_async_packet == p);
|
|
|
|
dev->endpoint[i].pending_async_packet = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
packet_id_queue_add(&dev->cancelled, p->id);
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_cancel_data_packet(dev->parser, p->id);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-08-28 11:30:13 +02:00
|
|
|
if (!dev->dev.attached) {
|
|
|
|
return 1; /* Treat everything as cancelled after a disconnect */
|
|
|
|
}
|
2012-09-12 15:08:34 +02:00
|
|
|
return packet_id_queue_remove(&dev->cancelled, id);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:35 +02:00
|
|
|
static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev,
|
|
|
|
struct USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
static USBPacket *p;
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
/* async handled packets for bulk receiving eps do not count as inflight */
|
|
|
|
if (dev->endpoint[USBEP2I(ep)].bulk_receiving_started) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:35 +02:00
|
|
|
QTAILQ_FOREACH(p, &ep->queue, queue) {
|
2012-10-31 13:47:12 +01:00
|
|
|
/* Skip combined packets, except for the first */
|
|
|
|
if (p->combined && p != p->combined->first) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-11-15 14:14:59 +01:00
|
|
|
if (p->state == USB_PACKET_ASYNC) {
|
|
|
|
packet_id_queue_add(&dev->already_in_flight, p->id);
|
|
|
|
}
|
2012-09-12 15:08:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_fill_already_in_flight(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
int ep;
|
|
|
|
struct USBDevice *udev = &dev->dev;
|
|
|
|
|
|
|
|
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl);
|
|
|
|
|
|
|
|
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
|
|
|
|
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
|
|
|
|
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id)
|
|
|
|
{
|
|
|
|
return packet_id_queue_remove(&dev->already_in_flight, id);
|
|
|
|
}
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
|
|
|
|
uint8_t ep, uint64_t id)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-08-28 11:30:13 +02:00
|
|
|
USBPacket *p;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
if (usbredir_is_cancelled(dev, id)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
p = usb_ep_find_packet_by_id(&dev->dev,
|
|
|
|
(ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT,
|
|
|
|
ep & 0x0f, id);
|
|
|
|
if (p == NULL) {
|
|
|
|
ERROR("could not find packet with id %"PRIu64"\n", id);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2012-08-28 11:30:13 +02:00
|
|
|
return p;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
|
|
|
|
uint8_t status, uint8_t ep, void *free_on_destroy)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-01-10 14:13:07 +01:00
|
|
|
struct buf_packet *bufp;
|
|
|
|
|
|
|
|
if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_size >
|
|
|
|
2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
|
|
|
|
DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
|
|
|
|
}
|
|
|
|
/* Since we're interupting the stream anyways, drop enough packets to get
|
|
|
|
back to our target buffer size */
|
|
|
|
if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
|
|
|
|
if (dev->endpoint[EP2I(ep)].bufpq_size >
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_target_size) {
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
|
|
|
|
}
|
|
|
|
|
2015-10-29 16:55:22 +01:00
|
|
|
bufp = g_new(struct buf_packet, 1);
|
2011-07-19 11:04:10 +02:00
|
|
|
bufp->data = data;
|
|
|
|
bufp->len = len;
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp->offset = 0;
|
2011-07-19 11:04:10 +02:00
|
|
|
bufp->status = status;
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp->free_on_destroy = free_on_destroy;
|
2011-07-19 11:04:10 +02:00
|
|
|
QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
|
2012-01-10 14:13:06 +01:00
|
|
|
dev->endpoint[EP2I(ep)].bufpq_size++;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
|
|
|
|
uint8_t ep)
|
|
|
|
{
|
|
|
|
QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
|
2012-01-10 14:13:06 +01:00
|
|
|
dev->endpoint[EP2I(ep)].bufpq_size--;
|
2012-12-19 15:08:33 +01:00
|
|
|
free(bufp->free_on_destroy);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(bufp);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
|
|
|
|
{
|
|
|
|
struct buf_packet *buf, *buf_next;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
|
|
|
|
bufp_free(dev, buf, ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* USBDevice callbacks
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void usbredir_handle_reset(USBDevice *udev)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
DPRINTF("reset device\n");
|
|
|
|
usbredirparser_send_reset(dev->parser);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
uint8_t ep)
|
|
|
|
{
|
|
|
|
int status, len;
|
|
|
|
if (!dev->endpoint[EP2I(ep)].iso_started &&
|
|
|
|
!dev->endpoint[EP2I(ep)].iso_error) {
|
|
|
|
struct usb_redir_start_iso_stream_header start_iso = {
|
|
|
|
.endpoint = ep,
|
|
|
|
};
|
2012-01-10 14:13:05 +01:00
|
|
|
int pkts_per_sec;
|
|
|
|
|
|
|
|
if (dev->dev.speed == USB_SPEED_HIGH) {
|
|
|
|
pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
|
|
|
|
} else {
|
|
|
|
pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
|
|
|
|
}
|
|
|
|
/* Testing has shown that we need circa 60 ms buffer */
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
|
|
|
|
|
|
|
|
/* Aim for approx 100 interrupts / second on the client to
|
|
|
|
balance latency and interrupt load */
|
|
|
|
start_iso.pkts_per_urb = pkts_per_sec / 100;
|
|
|
|
if (start_iso.pkts_per_urb < 1) {
|
|
|
|
start_iso.pkts_per_urb = 1;
|
|
|
|
} else if (start_iso.pkts_per_urb > 32) {
|
|
|
|
start_iso.pkts_per_urb = 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
|
|
|
|
start_iso.pkts_per_urb - 1) /
|
|
|
|
start_iso.pkts_per_urb;
|
|
|
|
/* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
|
|
|
|
as overflow buffer. Also see the usbredir protocol documentation */
|
|
|
|
if (!(ep & USB_DIR_IN)) {
|
|
|
|
start_iso.no_urbs *= 2;
|
|
|
|
}
|
|
|
|
if (start_iso.no_urbs > 16) {
|
|
|
|
start_iso.no_urbs = 16;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/* No id, we look at the ep when receiving a status back */
|
|
|
|
usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
2012-01-10 14:13:08 +01:00
|
|
|
DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
|
|
|
|
pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->endpoint[EP2I(ep)].iso_started = 1;
|
2012-01-10 14:13:06 +01:00
|
|
|
dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
|
2012-01-10 14:13:07 +01:00
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
|
struct buf_packet *isop;
|
|
|
|
|
2012-01-10 14:13:06 +01:00
|
|
|
if (dev->endpoint[EP2I(ep)].iso_started &&
|
|
|
|
!dev->endpoint[EP2I(ep)].bufpq_prefilled) {
|
|
|
|
if (dev->endpoint[EP2I(ep)].bufpq_size <
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_target_size) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
return;
|
2012-01-10 14:13:06 +01:00
|
|
|
}
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
|
|
|
|
if (isop == NULL) {
|
2012-01-10 14:13:08 +01:00
|
|
|
DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
|
|
|
|
ep, dev->endpoint[EP2I(ep)].iso_error);
|
2012-01-10 14:13:06 +01:00
|
|
|
/* Re-fill the buffer */
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
|
2011-07-19 11:04:10 +02:00
|
|
|
/* Check iso_error for stream errors, otherwise its an underrun */
|
|
|
|
status = dev->endpoint[EP2I(ep)].iso_error;
|
|
|
|
dev->endpoint[EP2I(ep)].iso_error = 0;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = status ? USB_RET_IOERROR : USB_RET_SUCCESS;
|
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2012-01-10 14:13:08 +01:00
|
|
|
DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
|
|
|
|
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
status = isop->status;
|
|
|
|
len = isop->len;
|
2011-07-12 15:22:25 +02:00
|
|
|
if (len > p->iov.size) {
|
2012-01-10 14:13:08 +01:00
|
|
|
ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
|
|
|
|
ep, len, (int)p->iov.size);
|
2012-11-01 17:15:02 +01:00
|
|
|
len = p->iov.size;
|
|
|
|
status = usb_redir_babble;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2011-07-12 15:22:25 +02:00
|
|
|
usb_packet_copy(p, isop->data, len);
|
2011-07-19 11:04:10 +02:00
|
|
|
bufp_free(dev, isop, ep);
|
2012-11-01 17:15:02 +01:00
|
|
|
usbredir_handle_status(dev, p, status);
|
2011-07-19 11:04:10 +02:00
|
|
|
} else {
|
|
|
|
/* If the stream was not started because of a pending error don't
|
|
|
|
send the packet to the usb-host */
|
|
|
|
if (dev->endpoint[EP2I(ep)].iso_started) {
|
|
|
|
struct usb_redir_iso_packet_header iso_packet = {
|
|
|
|
.endpoint = ep,
|
2011-07-12 15:22:25 +02:00
|
|
|
.length = p->iov.size
|
2011-07-19 11:04:10 +02:00
|
|
|
};
|
2011-07-12 15:22:25 +02:00
|
|
|
uint8_t buf[p->iov.size];
|
2011-07-19 11:04:10 +02:00
|
|
|
/* No id, we look at the ep when receiving a status back */
|
2011-07-12 15:22:25 +02:00
|
|
|
usb_packet_copy(p, buf, p->iov.size);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
|
2011-07-12 15:22:25 +02:00
|
|
|
buf, p->iov.size);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
status = dev->endpoint[EP2I(ep)].iso_error;
|
|
|
|
dev->endpoint[EP2I(ep)].iso_error = 0;
|
2011-07-12 15:22:25 +02:00
|
|
|
DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
|
|
|
|
p->iov.size);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_status(dev, p, status);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
|
|
|
|
{
|
|
|
|
struct usb_redir_stop_iso_stream_header stop_iso_stream = {
|
|
|
|
.endpoint = ep
|
|
|
|
};
|
|
|
|
if (dev->endpoint[EP2I(ep)].iso_started) {
|
|
|
|
usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
|
|
|
|
DPRINTF("iso stream stopped ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].iso_started = 0;
|
|
|
|
}
|
2012-01-10 14:13:04 +01:00
|
|
|
dev->endpoint[EP2I(ep)].iso_error = 0;
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredir_free_bufpq(dev, ep);
|
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
/*
|
|
|
|
* The usb-host may poll the endpoint faster then our guest, resulting in lots
|
|
|
|
* of smaller bulkp-s. The below buffered_bulk_in_complete* functions combine
|
|
|
|
* data from multiple bulkp-s into a single packet, avoiding bufpq overflows.
|
|
|
|
*/
|
|
|
|
static void usbredir_buffered_bulk_add_data_to_packet(USBRedirDevice *dev,
|
|
|
|
struct buf_packet *bulkp, int count, USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
usb_packet_copy(p, bulkp->data + bulkp->offset, count);
|
|
|
|
bulkp->offset += count;
|
|
|
|
if (bulkp->offset == bulkp->len) {
|
|
|
|
/* Store status in the last packet with data from this bulkp */
|
|
|
|
usbredir_handle_status(dev, p, bulkp->status);
|
|
|
|
bufp_free(dev, bulkp, ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_buffered_bulk_in_complete_raw(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
struct buf_packet *bulkp;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) &&
|
|
|
|
p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) {
|
|
|
|
count = bulkp->len - bulkp->offset;
|
|
|
|
if (count > (p->iov.size - p->actual_length)) {
|
|
|
|
count = p->iov.size - p->actual_length;
|
|
|
|
}
|
|
|
|
usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
const int maxp = dev->endpoint[EP2I(ep)].max_packet_size;
|
|
|
|
uint8_t header[2] = { 0, 0 };
|
|
|
|
struct buf_packet *bulkp;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) &&
|
|
|
|
p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) {
|
|
|
|
if (bulkp->len < 2) {
|
|
|
|
WARNING("malformed ftdi bulk in packet\n");
|
|
|
|
bufp_free(dev, bulkp, ep);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p->actual_length % maxp) == 0) {
|
|
|
|
usb_packet_copy(p, bulkp->data, 2);
|
|
|
|
memcpy(header, bulkp->data, 2);
|
|
|
|
} else {
|
|
|
|
if (bulkp->data[0] != header[0] || bulkp->data[1] != header[1]) {
|
|
|
|
break; /* Different header, add to next packet */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bulkp->offset == 0) {
|
|
|
|
bulkp->offset = 2; /* Skip header */
|
|
|
|
}
|
|
|
|
count = bulkp->len - bulkp->offset;
|
|
|
|
/* Must repeat the header at maxp interval */
|
|
|
|
if (count > (maxp - (p->actual_length % maxp))) {
|
|
|
|
count = maxp - (p->actual_length % maxp);
|
|
|
|
}
|
|
|
|
usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_buffered_bulk_in_complete(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
|
|
|
dev->buffered_bulk_in_complete(dev, p, ep);
|
|
|
|
DPRINTF("bulk-token-in ep %02X status %d len %d id %"PRIu64"\n",
|
|
|
|
ep, p->status, p->actual_length, p->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
/* Input bulk endpoint, buffered packet input */
|
|
|
|
if (!dev->endpoint[EP2I(ep)].bulk_receiving_started) {
|
|
|
|
int bpt;
|
|
|
|
struct usb_redir_start_bulk_receiving_header start = {
|
|
|
|
.endpoint = ep,
|
|
|
|
.stream_id = 0,
|
|
|
|
.no_transfers = 5,
|
|
|
|
};
|
|
|
|
/* Round bytes_per_transfer up to a multiple of max_packet_size */
|
|
|
|
bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1;
|
|
|
|
bpt /= dev->endpoint[EP2I(ep)].max_packet_size;
|
|
|
|
bpt *= dev->endpoint[EP2I(ep)].max_packet_size;
|
|
|
|
start.bytes_per_transfer = bpt;
|
|
|
|
/* No id, we look at the ep when receiving a status back */
|
|
|
|
usbredirparser_send_start_bulk_receiving(dev->parser, 0, &start);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
DPRINTF("bulk receiving started bytes/transfer %u count %d ep %02X\n",
|
|
|
|
start.bytes_per_transfer, start.no_transfers, ep);
|
|
|
|
dev->endpoint[EP2I(ep)].bulk_receiving_started = 1;
|
|
|
|
/* We don't really want to drop bulk packets ever, but
|
|
|
|
having some upper limit to how much we buffer is good. */
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_target_size = 5000;
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) {
|
|
|
|
DPRINTF("bulk-token-in ep %02X, no bulkp\n", ep);
|
|
|
|
assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL);
|
|
|
|
dev->endpoint[EP2I(ep)].pending_async_packet = p;
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
usbredir_buffered_bulk_in_complete(dev, p, ep);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_stop_bulk_receiving(USBRedirDevice *dev, uint8_t ep)
|
|
|
|
{
|
|
|
|
struct usb_redir_stop_bulk_receiving_header stop_bulk = {
|
|
|
|
.endpoint = ep,
|
|
|
|
.stream_id = 0,
|
|
|
|
};
|
|
|
|
if (dev->endpoint[EP2I(ep)].bulk_receiving_started) {
|
|
|
|
usbredirparser_send_stop_bulk_receiving(dev->parser, 0, &stop_bulk);
|
|
|
|
DPRINTF("bulk receiving stopped ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].bulk_receiving_started = 0;
|
|
|
|
}
|
|
|
|
usbredir_free_bufpq(dev, ep);
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
uint8_t ep)
|
|
|
|
{
|
|
|
|
struct usb_redir_bulk_packet_header bulk_packet;
|
2013-01-24 15:40:37 +01:00
|
|
|
size_t size = usb_packet_size(p);
|
2012-12-19 15:08:33 +01:00
|
|
|
const int maxp = dev->endpoint[EP2I(ep)].max_packet_size;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-12 15:08:35 +02:00
|
|
|
if (usbredir_already_in_flight(dev, p->id)) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
return;
|
2012-09-12 15:08:35 +02:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) {
|
|
|
|
if (size != 0 && (size % maxp) == 0) {
|
|
|
|
usbredir_handle_buffered_bulk_in_data(dev, p, ep);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
WARNING("bulk recv invalid size %zd ep %02x, disabling\n", size, ep);
|
|
|
|
assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL);
|
|
|
|
usbredir_stop_bulk_receiving(dev, ep);
|
|
|
|
dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n",
|
|
|
|
ep, p->stream, size, p->id);
|
2012-12-19 15:08:33 +01:00
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
bulk_packet.endpoint = ep;
|
2012-10-31 13:47:12 +01:00
|
|
|
bulk_packet.length = size;
|
2013-11-19 14:37:03 +01:00
|
|
|
bulk_packet.stream_id = p->stream;
|
2012-10-31 13:47:12 +01:00
|
|
|
bulk_packet.length_high = size >> 16;
|
2012-10-31 13:47:11 +01:00
|
|
|
assert(bulk_packet.length_high == 0 ||
|
|
|
|
usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_32bits_bulk_length));
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
if (ep & USB_DIR_IN) {
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
2011-07-19 11:04:10 +02:00
|
|
|
&bulk_packet, NULL, 0);
|
|
|
|
} else {
|
2012-10-31 13:47:12 +01:00
|
|
|
uint8_t buf[size];
|
2013-01-24 15:40:37 +01:00
|
|
|
usb_packet_copy(p, buf, size);
|
2012-10-31 13:47:12 +01:00
|
|
|
usbredir_log_data(dev, "bulk data out:", buf, size);
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
2012-10-31 13:47:12 +01:00
|
|
|
&bulk_packet, buf, size);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-11-17 12:26:56 +01:00
|
|
|
static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-11-17 12:26:56 +01:00
|
|
|
/* Input interrupt endpoint, buffered packet input */
|
|
|
|
struct buf_packet *intp;
|
|
|
|
int status, len;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-11-17 12:26:56 +01:00
|
|
|
if (!dev->endpoint[EP2I(ep)].interrupt_started &&
|
|
|
|
!dev->endpoint[EP2I(ep)].interrupt_error) {
|
|
|
|
struct usb_redir_start_interrupt_receiving_header start_int = {
|
|
|
|
.endpoint = ep,
|
|
|
|
};
|
|
|
|
/* No id, we look at the ep when receiving a status back */
|
|
|
|
usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
|
|
|
|
&start_int);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
DPRINTF("interrupt recv started ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].interrupt_started = 1;
|
|
|
|
/* We don't really want to drop interrupt packets ever, but
|
|
|
|
having some upper limit to how much we buffer is good. */
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
|
|
|
|
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-11-17 12:26:56 +01:00
|
|
|
intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
|
|
|
|
if (intp == NULL) {
|
|
|
|
DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
|
|
|
|
/* Check interrupt_error for stream errors */
|
|
|
|
status = dev->endpoint[EP2I(ep)].interrupt_error;
|
|
|
|
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
|
|
|
if (status) {
|
|
|
|
usbredir_handle_status(dev, p, status);
|
|
|
|
} else {
|
|
|
|
p->status = USB_RET_NAK;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2012-11-17 12:26:56 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
usb_packet_copy(p, intp->data, len);
|
|
|
|
bufp_free(dev, intp, ep);
|
|
|
|
usbredir_handle_status(dev, p, status);
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
|
usb-redir: Don't handle interrupt output packets async
Instead report them as successfully completed directly on submission, this
has 2 advantages:
1) This matches the timing of interrupt output packets on real hardware,
with the previous async handling, if an ep has an interval of say 500 ms,
then there would be 500+ ms between the submission and the guest seeing the
completion, as we wont do the write back until the qh gets polled again. And
in the mean time the guest may very well have timed out, as the guest can
reasonable expect a much quicker completion.
2) This fixes interrupt output packets potentially getting send twice
surrounding a migration. As we delay the writeback to guest memory until
the qh gets polled again, there is a window between completion and writeback
where migration can happen, in this case the destination will not know
about the completion, and it will execute the packet *again*
But it does also come with a disadvantage:
1) If the actual interrupt out to the real usb device fails, there is no
way to report this back to the guest.
This patch assumes however that interrupt outs in practice never fail, as
they are only used by specialized drivers, which are unlikely to issue illegal
requests (unlike general class drivers which often issue requests which some
devices don't implement). And that thus the advantages outway the disadvantage.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-17 12:26:57 +01:00
|
|
|
/*
|
|
|
|
* Handle interrupt out data, the usbredir protocol expects us to do this
|
|
|
|
* async, so that it can report back a completion status. But guests will
|
|
|
|
* expect immediate completion for an interrupt endpoint, and handling this
|
|
|
|
* async causes migration issues. So we report success directly, counting
|
|
|
|
* on the fact that output interrupt packets normally always succeed.
|
|
|
|
*/
|
2012-11-17 12:26:56 +01:00
|
|
|
static void usbredir_handle_interrupt_out_data(USBRedirDevice *dev,
|
|
|
|
USBPacket *p, uint8_t ep)
|
|
|
|
{
|
|
|
|
struct usb_redir_interrupt_packet_header interrupt_packet;
|
|
|
|
uint8_t buf[p->iov.size];
|
2012-09-12 15:08:35 +02:00
|
|
|
|
2012-11-17 12:26:56 +01:00
|
|
|
DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
|
|
|
|
p->iov.size, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-11-17 12:26:56 +01:00
|
|
|
interrupt_packet.endpoint = ep;
|
|
|
|
interrupt_packet.length = p->iov.size;
|
|
|
|
|
|
|
|
usb_packet_copy(p, buf, p->iov.size);
|
|
|
|
usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
|
|
|
|
usbredirparser_send_interrupt_packet(dev->parser, p->id,
|
|
|
|
&interrupt_packet, buf, p->iov.size);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
|
|
|
|
uint8_t ep)
|
|
|
|
{
|
|
|
|
struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
|
|
|
|
.endpoint = ep
|
|
|
|
};
|
|
|
|
if (dev->endpoint[EP2I(ep)].interrupt_started) {
|
|
|
|
usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
|
|
|
|
&stop_interrupt_recv);
|
|
|
|
DPRINTF("interrupt recv stopped ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].interrupt_started = 0;
|
|
|
|
}
|
2012-01-10 14:13:04 +01:00
|
|
|
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredir_free_bufpq(dev, ep);
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_data(USBDevice *udev, USBPacket *p)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2011-07-19 11:04:10 +02:00
|
|
|
uint8_t ep;
|
|
|
|
|
2012-01-12 13:23:01 +01:00
|
|
|
ep = p->ep->nr;
|
2011-07-19 11:04:10 +02:00
|
|
|
if (p->pid == USB_TOKEN_IN) {
|
|
|
|
ep |= USB_DIR_IN;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dev->endpoint[EP2I(ep)].type) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
ERROR("handle_data called for control transfer on ep %02X\n", ep);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
2012-10-31 13:47:12 +01:00
|
|
|
if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN &&
|
|
|
|
p->ep->pipeline) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ADD_TO_QUEUE;
|
|
|
|
break;
|
2012-10-31 13:47:12 +01:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_bulk_data(dev, p, ep);
|
|
|
|
break;
|
2012-12-19 15:08:33 +01:00
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
usbredir_handle_iso_data(dev, p, ep);
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case USB_ENDPOINT_XFER_INT:
|
2012-11-17 12:26:56 +01:00
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
|
usbredir_handle_interrupt_in_data(dev, p, ep);
|
|
|
|
} else {
|
|
|
|
usbredir_handle_interrupt_out_data(dev, p, ep);
|
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
default:
|
|
|
|
ERROR("handle_data ep %02X has unknown type %d\n", ep,
|
|
|
|
dev->endpoint[EP2I(ep)].type);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_NAK;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-31 13:47:12 +01:00
|
|
|
static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
if (ep->pid == USB_TOKEN_IN && ep->pipeline) {
|
|
|
|
usb_ep_combine_input_packets(ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-14 14:35:41 +01:00
|
|
|
static void usbredir_stop_ep(USBRedirDevice *dev, int i)
|
|
|
|
{
|
|
|
|
uint8_t ep = I2EP(i);
|
|
|
|
|
|
|
|
switch (dev->endpoint[i].type) {
|
2012-12-19 15:08:33 +01:00
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
|
usbredir_stop_bulk_receiving(dev, ep);
|
|
|
|
}
|
|
|
|
break;
|
2012-12-14 14:35:41 +01:00
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
usbredir_stop_iso_stream(dev, ep);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
|
usbredir_stop_interrupt_receiving(dev, ep);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
usbredir_free_bufpq(dev, ep);
|
|
|
|
}
|
|
|
|
|
2012-12-14 14:35:43 +01:00
|
|
|
static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2012-12-14 14:35:43 +01:00
|
|
|
|
|
|
|
usbredir_stop_ep(dev, USBEP2I(uep));
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
int config)
|
|
|
|
{
|
|
|
|
struct usb_redir_set_configuration_header set_config;
|
|
|
|
int i;
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
DPRINTF("set config %d id %"PRIu64"\n", config, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
2012-12-14 14:35:41 +01:00
|
|
|
usbredir_stop_ep(dev, i);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
set_config.configuration = config;
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-08-28 11:30:13 +02:00
|
|
|
DPRINTF("get config id %"PRIu64"\n", p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_get_configuration(dev->parser, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
int interface, int alt)
|
|
|
|
{
|
|
|
|
struct usb_redir_set_alt_setting_header set_alt;
|
|
|
|
int i;
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
|
|
if (dev->endpoint[i].interface == interface) {
|
2012-12-14 14:35:41 +01:00
|
|
|
usbredir_stop_ep(dev, i);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
set_alt.interface = interface;
|
|
|
|
set_alt.alt = alt;
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
int interface)
|
|
|
|
{
|
|
|
|
struct usb_redir_get_alt_setting_header get_alt;
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
get_alt.interface = interface;
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
2011-07-19 11:04:10 +02:00
|
|
|
int request, int value, int index, int length, uint8_t *data)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_control_packet_header control_packet;
|
|
|
|
|
2012-09-12 15:08:35 +02:00
|
|
|
if (usbredir_already_in_flight(dev, p->id)) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
return;
|
2012-09-12 15:08:35 +02:00
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/* Special cases for certain standard device requests */
|
|
|
|
switch (request) {
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
|
|
DPRINTF("set address %d\n", value);
|
|
|
|
dev->dev.addr = value;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_set_config(dev, p, value & 0xff);
|
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_get_config(dev, p);
|
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_set_interface(dev, p, index, value);
|
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_get_interface(dev, p, index);
|
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
/* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
|
|
|
|
DPRINTF(
|
|
|
|
"ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n",
|
|
|
|
request >> 8, request & 0xff, value, index, length, p->id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
control_packet.request = request & 0xFF;
|
|
|
|
control_packet.requesttype = request >> 8;
|
|
|
|
control_packet.endpoint = control_packet.requesttype & USB_DIR_IN;
|
|
|
|
control_packet.value = value;
|
|
|
|
control_packet.index = index;
|
|
|
|
control_packet.length = length;
|
|
|
|
|
|
|
|
if (control_packet.requesttype & USB_DIR_IN) {
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_control_packet(dev->parser, p->id,
|
2011-07-19 11:04:10 +02:00
|
|
|
&control_packet, NULL, 0);
|
|
|
|
} else {
|
|
|
|
usbredir_log_data(dev, "ctrl data out:", data, length);
|
2012-08-28 11:30:13 +02:00
|
|
|
usbredirparser_send_control_packet(dev->parser, p->id,
|
2011-07-19 11:04:10 +02:00
|
|
|
&control_packet, data, length);
|
|
|
|
}
|
|
|
|
usbredirparser_do_write(dev->parser);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_ASYNC;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps,
|
|
|
|
int nr_eps, int streams)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2013-11-19 14:37:03 +01:00
|
|
|
#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
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2013-11-19 14:37:03 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/*
|
|
|
|
* Close events can be triggered by usbredirparser_do_write which gets called
|
|
|
|
* from within the USBDevice data / control packet callbacks and doing a
|
|
|
|
* usb_detach from within these callbacks is not a good idea.
|
|
|
|
*
|
2012-08-23 16:37:19 +02:00
|
|
|
* So we use a bh handler to take care of close events.
|
2011-07-19 11:04:10 +02:00
|
|
|
*/
|
2012-08-23 16:37:19 +02:00
|
|
|
static void usbredir_chardev_close_bh(void *opaque)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
qemu_bh_cancel(dev->device_reject_bh);
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredir_device_disconnect(dev);
|
|
|
|
|
|
|
|
if (dev->parser) {
|
2012-09-12 15:08:38 +02:00
|
|
|
DPRINTF("destroying usbredirparser\n");
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredirparser_destroy(dev->parser);
|
|
|
|
dev->parser = NULL;
|
|
|
|
}
|
2013-03-19 14:54:38 +01:00
|
|
|
if (dev->watch) {
|
|
|
|
g_source_remove(dev->watch);
|
|
|
|
dev->watch = 0;
|
|
|
|
}
|
2012-08-23 16:37:19 +02:00
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-27 17:25:44 +02:00
|
|
|
static void usbredir_create_parser(USBRedirDevice *dev)
|
2012-08-23 16:37:19 +02:00
|
|
|
{
|
|
|
|
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
|
2012-09-12 15:08:37 +02:00
|
|
|
int flags = 0;
|
2012-01-25 09:50:39 +01:00
|
|
|
|
2012-09-12 15:08:38 +02:00
|
|
|
DPRINTF("creating usbredirparser\n");
|
|
|
|
|
2012-08-23 16:37:19 +02:00
|
|
|
dev->parser = qemu_oom_check(usbredirparser_create());
|
|
|
|
dev->parser->priv = dev;
|
|
|
|
dev->parser->log_func = usbredir_log;
|
|
|
|
dev->parser->read_func = usbredir_read;
|
|
|
|
dev->parser->write_func = usbredir_write;
|
|
|
|
dev->parser->hello_func = usbredir_hello;
|
|
|
|
dev->parser->device_connect_func = usbredir_device_connect;
|
|
|
|
dev->parser->device_disconnect_func = usbredir_device_disconnect;
|
|
|
|
dev->parser->interface_info_func = usbredir_interface_info;
|
|
|
|
dev->parser->ep_info_func = usbredir_ep_info;
|
|
|
|
dev->parser->configuration_status_func = usbredir_configuration_status;
|
|
|
|
dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
|
|
|
|
dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
|
|
|
|
dev->parser->interrupt_receiving_status_func =
|
|
|
|
usbredir_interrupt_receiving_status;
|
|
|
|
dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
|
2012-12-19 15:08:33 +01:00
|
|
|
dev->parser->bulk_receiving_status_func = usbredir_bulk_receiving_status;
|
2012-08-23 16:37:19 +02:00
|
|
|
dev->parser->control_packet_func = usbredir_control_packet;
|
|
|
|
dev->parser->bulk_packet_func = usbredir_bulk_packet;
|
|
|
|
dev->parser->iso_packet_func = usbredir_iso_packet;
|
|
|
|
dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
|
2012-12-19 15:08:33 +01:00
|
|
|
dev->parser->buffered_bulk_packet_func = usbredir_buffered_bulk_packet;
|
2012-08-23 16:37:19 +02:00
|
|
|
dev->read_buf = NULL;
|
|
|
|
dev->read_buf_size = 0;
|
|
|
|
|
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
2012-09-03 11:49:07 +02:00
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
|
2012-08-31 13:41:38 +02:00
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
|
2012-10-31 13:47:11 +01:00
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
|
2012-12-19 15:08:33 +01:00
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
|
2013-11-19 14:37:03 +01:00
|
|
|
#if USBREDIR_VERSION >= 0x000700
|
|
|
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
|
|
|
|
#endif
|
2012-09-12 15:08:37 +02:00
|
|
|
|
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
flags |= usbredirparser_fl_no_hello;
|
|
|
|
}
|
2012-09-12 15:08:39 +02:00
|
|
|
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
|
2012-09-12 15:08:37 +02:00
|
|
|
flags);
|
2012-08-23 16:37:19 +02:00
|
|
|
usbredirparser_do_write(dev->parser);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-03 11:53:28 +02:00
|
|
|
static void usbredir_reject_device(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
usbredir_device_disconnect(dev);
|
|
|
|
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
|
|
|
|
usbredirparser_send_filter_reject(dev->parser);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
static void usbredir_do_attach(void *opaque)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
2014-09-19 08:48:24 +02:00
|
|
|
Error *local_err = NULL;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-03 12:04:49 +02:00
|
|
|
/* In order to work properly with XHCI controllers we need these caps */
|
|
|
|
if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !(
|
|
|
|
usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_ep_info_max_packet_size) &&
|
2012-12-14 14:35:44 +01:00
|
|
|
usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_32bits_bulk_length) &&
|
2012-09-03 12:04:49 +02:00
|
|
|
usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_64bits_ids))) {
|
|
|
|
ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n");
|
|
|
|
usbredir_reject_device(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-19 08:48:24 +02:00
|
|
|
usb_device_attach(&dev->dev, &local_err);
|
|
|
|
if (local_err) {
|
2015-02-12 13:55:05 +01:00
|
|
|
error_report_err(local_err);
|
2012-10-31 13:47:15 +01:00
|
|
|
WARNING("rejecting device due to speed mismatch\n");
|
2012-09-03 11:53:28 +02:00
|
|
|
usbredir_reject_device(dev);
|
2012-03-30 09:53:54 +02:00
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* chardev callbacks
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int usbredir_chardev_can_read(void *opaque)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
|
|
|
|
2012-08-23 16:37:19 +02:00
|
|
|
if (!dev->parser) {
|
|
|
|
WARNING("chardev_can_read called on non open chardev!\n");
|
2011-07-19 11:04:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-08-23 16:37:19 +02:00
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
/* Don't read new data from the chardev until our state is fully synced */
|
|
|
|
if (!runstate_check(RUN_STATE_RUNNING)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-23 16:37:19 +02:00
|
|
|
/* usbredir_parser_do_read will consume *all* data we give it */
|
|
|
|
return 1024 * 1024;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
|
|
|
|
|
|
|
/* No recursion allowed! */
|
|
|
|
assert(dev->read_buf == NULL);
|
|
|
|
|
|
|
|
dev->read_buf = buf;
|
|
|
|
dev->read_buf_size = size;
|
|
|
|
|
|
|
|
usbredirparser_do_read(dev->parser);
|
|
|
|
/* Send any acks, etc. which may be queued now */
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_chardev_event(void *opaque, int event)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = opaque;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_OPENED:
|
2012-09-12 15:08:38 +02:00
|
|
|
DPRINTF("chardev open\n");
|
2012-09-27 17:25:44 +02:00
|
|
|
/* Make sure any pending closes are handled (no-op if none pending) */
|
|
|
|
usbredir_chardev_close_bh(dev);
|
|
|
|
qemu_bh_cancel(dev->chardev_close_bh);
|
|
|
|
usbredir_create_parser(dev);
|
2012-08-23 16:37:19 +02:00
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case CHR_EVENT_CLOSED:
|
2012-09-12 15:08:38 +02:00
|
|
|
DPRINTF("chardev close\n");
|
2012-08-23 16:37:19 +02:00
|
|
|
qemu_bh_schedule(dev->chardev_close_bh);
|
2011-07-19 11:04:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* init + destroy
|
|
|
|
*/
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
static void usbredir_vm_state_change(void *priv, int running, RunState state)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
if (state == RUN_STATE_RUNNING && dev->parser != NULL) {
|
|
|
|
usbredirparser_do_write(dev->parser); /* Flush any pending writes */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-14 14:35:45 +01:00
|
|
|
static void usbredir_init_endpoints(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
usb_ep_init(&dev->dev);
|
|
|
|
memset(dev->endpoint, 0, sizeof(dev->endpoint));
|
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
2012-12-14 14:35:46 +01:00
|
|
|
dev->endpoint[i].dev = dev;
|
2012-12-14 14:35:45 +01:00
|
|
|
QTAILQ_INIT(&dev->endpoint[i].bufpq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 08:48:39 +02:00
|
|
|
static void usbredir_realize(USBDevice *udev, Error **errp)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2011-07-19 11:04:10 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (dev->cs == NULL) {
|
2015-03-17 11:54:50 +01:00
|
|
|
error_setg(errp, QERR_MISSING_PARAMETER, "chardev");
|
2014-09-19 08:48:39 +02:00
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-01-25 09:50:39 +01:00
|
|
|
if (dev->filter_str) {
|
|
|
|
i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|",
|
|
|
|
&dev->filter_rules,
|
|
|
|
&dev->filter_rules_count);
|
|
|
|
if (i) {
|
2015-03-17 11:54:50 +01:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filter",
|
|
|
|
"a usb device filter string");
|
2014-09-19 08:48:39 +02:00
|
|
|
return;
|
2012-01-25 09:50:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 16:37:19 +02:00
|
|
|
dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
|
2013-11-19 14:37:03 +01:00
|
|
|
dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev);
|
2013-08-21 17:03:08 +02:00
|
|
|
dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
packet_id_queue_init(&dev->cancelled, dev, "cancelled");
|
2012-09-12 15:08:35 +02:00
|
|
|
packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight");
|
2012-12-14 14:35:45 +01:00
|
|
|
usbredir_init_endpoints(dev);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
/* We'll do the attach once we receive the speed from the usb-host */
|
|
|
|
udev->auto_attach = 0;
|
|
|
|
|
2012-10-31 13:47:15 +01:00
|
|
|
/* Will be cleared during setup when we find conflicts */
|
2012-10-31 13:47:16 +01:00
|
|
|
dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
|
2012-10-31 13:47:15 +01:00
|
|
|
|
2011-11-19 10:22:45 +01:00
|
|
|
/* Let the backend know we are ready */
|
2011-07-19 11:04:10 +02:00
|
|
|
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
|
|
|
|
usbredir_chardev_read, usbredir_chardev_event, dev);
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2012-09-12 15:08:34 +02:00
|
|
|
packet_id_queue_empty(&dev->cancelled);
|
2012-09-12 15:08:35 +02:00
|
|
|
packet_id_queue_empty(&dev->already_in_flight);
|
2011-07-19 11:04:10 +02:00
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
|
|
usbredir_free_bufpq(dev, I2EP(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_handle_destroy(USBDevice *udev)
|
|
|
|
{
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2011-08-15 18:17:38 +02:00
|
|
|
qemu_chr_delete(dev->cs);
|
2013-07-31 11:17:58 +02:00
|
|
|
dev->cs = NULL;
|
2011-07-19 11:04:10 +02:00
|
|
|
/* Note must be done after qemu_chr_close, as that causes a close event */
|
2012-08-23 16:37:19 +02:00
|
|
|
qemu_bh_delete(dev->chardev_close_bh);
|
2013-11-19 14:37:03 +01:00
|
|
|
qemu_bh_delete(dev->device_reject_bh);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_del(dev->attach_timer);
|
|
|
|
timer_free(dev->attach_timer);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
usbredir_cleanup_device_queues(dev);
|
|
|
|
|
|
|
|
if (dev->parser) {
|
|
|
|
usbredirparser_destroy(dev->parser);
|
|
|
|
}
|
2013-03-19 14:54:38 +01:00
|
|
|
if (dev->watch) {
|
|
|
|
g_source_remove(dev->watch);
|
|
|
|
}
|
2012-01-25 09:50:39 +01:00
|
|
|
|
|
|
|
free(dev->filter_rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_check_filter(USBRedirDevice *dev)
|
|
|
|
{
|
2012-03-31 17:14:21 +02:00
|
|
|
if (dev->interface_info.interface_count == NO_INTERFACE_INFO) {
|
2012-01-25 09:50:39 +01:00
|
|
|
ERROR("No interface info for device\n");
|
2012-02-26 16:14:44 +01:00
|
|
|
goto error;
|
2012-01-25 09:50:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->filter_rules) {
|
|
|
|
if (!usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_connect_device_version)) {
|
|
|
|
ERROR("Device filter specified and peer does not have the "
|
|
|
|
"connect_device_version capability\n");
|
2012-02-26 16:14:44 +01:00
|
|
|
goto error;
|
2012-01-25 09:50:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (usbredirfilter_check(
|
|
|
|
dev->filter_rules,
|
|
|
|
dev->filter_rules_count,
|
|
|
|
dev->device_info.device_class,
|
|
|
|
dev->device_info.device_subclass,
|
|
|
|
dev->device_info.device_protocol,
|
|
|
|
dev->interface_info.interface_class,
|
|
|
|
dev->interface_info.interface_subclass,
|
|
|
|
dev->interface_info.interface_protocol,
|
|
|
|
dev->interface_info.interface_count,
|
|
|
|
dev->device_info.vendor_id,
|
|
|
|
dev->device_info.product_id,
|
|
|
|
dev->device_info.device_version_bcd,
|
|
|
|
0) != 0) {
|
2012-02-26 16:14:44 +01:00
|
|
|
goto error;
|
2012-01-25 09:50:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-02-26 16:14:44 +01:00
|
|
|
|
|
|
|
error:
|
2012-09-03 11:53:28 +02:00
|
|
|
usbredir_reject_device(dev);
|
2012-02-26 16:14:44 +01:00
|
|
|
return -1;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
static void usbredir_check_bulk_receiving(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
int i, j, quirks;
|
|
|
|
|
|
|
|
if (!usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_bulk_receiving)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = EP2I(USB_DIR_IN); i < MAX_ENDPOINTS; i++) {
|
|
|
|
dev->endpoint[i].bulk_receiving_enabled = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < dev->interface_info.interface_count; i++) {
|
|
|
|
quirks = usb_get_quirks(dev->device_info.vendor_id,
|
|
|
|
dev->device_info.product_id,
|
|
|
|
dev->interface_info.interface_class[i],
|
|
|
|
dev->interface_info.interface_subclass[i],
|
|
|
|
dev->interface_info.interface_protocol[i]);
|
|
|
|
if (!(quirks & USB_QUIRK_BUFFER_BULK_IN)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (quirks & USB_QUIRK_IS_FTDI) {
|
|
|
|
dev->buffered_bulk_in_complete =
|
|
|
|
usbredir_buffered_bulk_in_complete_ftdi;
|
|
|
|
} else {
|
|
|
|
dev->buffered_bulk_in_complete =
|
|
|
|
usbredir_buffered_bulk_in_complete_raw;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = EP2I(USB_DIR_IN); j < MAX_ENDPOINTS; j++) {
|
|
|
|
if (dev->endpoint[j].interface ==
|
|
|
|
dev->interface_info.interface[i] &&
|
|
|
|
dev->endpoint[j].type == USB_ENDPOINT_XFER_BULK &&
|
|
|
|
dev->endpoint[j].max_packet_size != 0) {
|
|
|
|
dev->endpoint[j].bulk_receiving_enabled = 1;
|
|
|
|
/*
|
|
|
|
* With buffering pipelining is not necessary. Also packet
|
|
|
|
* combining and bulk in buffering don't play nice together!
|
|
|
|
*/
|
|
|
|
I2USBEP(dev, j)->pipeline = false;
|
|
|
|
break; /* Only buffer for the first ep of each intf */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/*
|
|
|
|
* usbredirparser packet complete callbacks
|
|
|
|
*/
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
|
|
|
|
int status)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case usb_redir_success:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case usb_redir_stall:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case usb_redir_cancelled:
|
2012-08-17 17:27:08 +02:00
|
|
|
/*
|
|
|
|
* When the usbredir-host unredirects a device, it will report a status
|
|
|
|
* of cancelled for all pending packets, followed by a disconnect msg.
|
|
|
|
*/
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_IOERROR;
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case usb_redir_inval:
|
2012-03-02 21:27:20 +01:00
|
|
|
WARNING("got invalid param error from usb-host?\n");
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_IOERROR;
|
|
|
|
break;
|
2012-07-06 12:09:32 +02:00
|
|
|
case usb_redir_babble:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_BABBLE;
|
|
|
|
break;
|
2011-07-19 11:04:10 +02:00
|
|
|
case usb_redir_ioerror:
|
|
|
|
case usb_redir_timeout:
|
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_IOERROR;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-26 16:14:45 +01:00
|
|
|
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
/* Try to send the filter info now that we've the usb-host's caps */
|
|
|
|
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
|
|
|
|
dev->filter_rules) {
|
|
|
|
usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
|
|
|
|
dev->filter_rules_count);
|
|
|
|
usbredirparser_do_write(dev->parser);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
static void usbredir_device_connect(void *priv,
|
|
|
|
struct usb_redir_device_connect_header *device_connect)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
2012-01-25 09:50:39 +01:00
|
|
|
const char *speed;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2013-08-21 17:02:39 +02:00
|
|
|
if (timer_pending(dev->attach_timer) || dev->dev.attached) {
|
2011-11-19 10:22:46 +01:00
|
|
|
ERROR("Received device connect while already connected\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
switch (device_connect->speed) {
|
|
|
|
case usb_redir_speed_low:
|
2012-01-25 09:50:39 +01:00
|
|
|
speed = "low speed";
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.speed = USB_SPEED_LOW;
|
2012-10-31 13:47:15 +01:00
|
|
|
dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL;
|
2012-10-31 13:47:16 +01:00
|
|
|
dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
|
2011-07-19 11:04:10 +02:00
|
|
|
break;
|
|
|
|
case usb_redir_speed_full:
|
2012-01-25 09:50:39 +01:00
|
|
|
speed = "full speed";
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.speed = USB_SPEED_FULL;
|
2012-10-31 13:47:16 +01:00
|
|
|
dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
|
2011-07-19 11:04:10 +02:00
|
|
|
break;
|
|
|
|
case usb_redir_speed_high:
|
2012-01-25 09:50:39 +01:00
|
|
|
speed = "high speed";
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.speed = USB_SPEED_HIGH;
|
|
|
|
break;
|
|
|
|
case usb_redir_speed_super:
|
2012-01-25 09:50:39 +01:00
|
|
|
speed = "super speed";
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.speed = USB_SPEED_SUPER;
|
|
|
|
break;
|
|
|
|
default:
|
2012-01-25 09:50:39 +01:00
|
|
|
speed = "unknown speed";
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.speed = USB_SPEED_FULL;
|
|
|
|
}
|
2012-01-25 09:50:39 +01:00
|
|
|
|
|
|
|
if (usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_connect_device_version)) {
|
|
|
|
INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
|
|
|
|
speed, device_connect->vendor_id, device_connect->product_id,
|
2012-02-26 16:14:43 +01:00
|
|
|
((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
|
|
|
|
((device_connect->device_version_bcd & 0x0f00) >> 8),
|
|
|
|
((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 +
|
|
|
|
((device_connect->device_version_bcd & 0x000f) >> 0),
|
2012-01-25 09:50:39 +01:00
|
|
|
device_connect->device_class);
|
|
|
|
} else {
|
|
|
|
INFO("attaching %s device %04x:%04x class %02x\n", speed,
|
|
|
|
device_connect->vendor_id, device_connect->product_id,
|
|
|
|
device_connect->device_class);
|
|
|
|
}
|
|
|
|
|
2012-10-31 13:47:15 +01:00
|
|
|
dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
|
2012-01-25 09:50:39 +01:00
|
|
|
dev->device_info = *device_connect;
|
|
|
|
|
|
|
|
if (usbredir_check_filter(dev)) {
|
|
|
|
WARNING("Device %04x:%04x rejected by device filter, not attaching\n",
|
|
|
|
device_connect->vendor_id, device_connect->product_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
usbredir_check_bulk_receiving(dev);
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_mod(dev->attach_timer, dev->next_attach_time);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_device_disconnect(void *priv)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
/* Stop any pending attaches */
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_del(dev->attach_timer);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
if (dev->dev.attached) {
|
2012-09-12 15:08:38 +02:00
|
|
|
DPRINTF("detaching device\n");
|
2011-07-19 11:04:10 +02:00
|
|
|
usb_device_detach(&dev->dev);
|
|
|
|
/*
|
|
|
|
* Delay next usb device attach to give the guest a chance to see
|
|
|
|
* see the detach / attach in case of quick close / open succession
|
|
|
|
*/
|
2013-08-21 17:03:08 +02:00
|
|
|
dev->next_attach_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 200;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2011-11-19 10:22:46 +01:00
|
|
|
|
|
|
|
/* Reset state so that the next dev connected starts with a clean slate */
|
|
|
|
usbredir_cleanup_device_queues(dev);
|
2012-12-14 14:35:45 +01:00
|
|
|
usbredir_init_endpoints(dev);
|
2012-03-31 17:14:21 +02:00
|
|
|
dev->interface_info.interface_count = NO_INTERFACE_INFO;
|
2012-03-31 17:14:22 +02:00
|
|
|
dev->dev.addr = 0;
|
|
|
|
dev->dev.speed = 0;
|
2012-10-31 13:47:16 +01:00
|
|
|
dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbredir_interface_info(void *priv,
|
|
|
|
struct usb_redir_interface_info_header *interface_info)
|
|
|
|
{
|
2012-01-25 09:50:39 +01:00
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
dev->interface_info = *interface_info;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we receive interface info after the device has already been
|
2012-12-19 15:08:33 +01:00
|
|
|
* connected (ie on a set_config), re-check interface dependent things.
|
2012-01-25 09:50:39 +01:00
|
|
|
*/
|
2013-08-21 17:02:39 +02:00
|
|
|
if (timer_pending(dev->attach_timer) || dev->dev.attached) {
|
2012-12-19 15:08:33 +01:00
|
|
|
usbredir_check_bulk_receiving(dev);
|
2012-01-25 09:50:39 +01:00
|
|
|
if (usbredir_check_filter(dev)) {
|
|
|
|
ERROR("Device no longer matches filter after interface info "
|
|
|
|
"change, disconnecting!\n");
|
|
|
|
}
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-10-31 13:47:15 +01:00
|
|
|
static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed)
|
|
|
|
{
|
|
|
|
dev->compatible_speedmask &= ~(1 << speed);
|
|
|
|
dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
|
|
|
|
}
|
|
|
|
|
2012-10-24 18:14:09 +02:00
|
|
|
static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
|
|
|
|
{
|
|
|
|
if (uep->type != USB_ENDPOINT_XFER_BULK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (uep->pid == USB_TOKEN_OUT) {
|
|
|
|
uep->pipeline = true;
|
|
|
|
}
|
2012-10-31 13:47:12 +01:00
|
|
|
if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 &&
|
|
|
|
usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_32bits_bulk_length)) {
|
|
|
|
uep->pipeline = true;
|
|
|
|
}
|
2012-10-24 18:14:09 +02:00
|
|
|
}
|
|
|
|
|
2012-10-31 13:47:13 +01:00
|
|
|
static void usbredir_setup_usb_eps(USBRedirDevice *dev)
|
|
|
|
{
|
|
|
|
struct USBEndpoint *usb_ep;
|
2012-12-14 14:35:42 +01:00
|
|
|
int i;
|
2012-10-31 13:47:13 +01:00
|
|
|
|
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
2012-12-14 14:35:42 +01:00
|
|
|
usb_ep = I2USBEP(dev, i);
|
2012-10-31 13:47:13 +01:00
|
|
|
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;
|
2013-11-19 14:37:03 +01:00
|
|
|
usb_ep->max_streams = dev->endpoint[i].max_streams;
|
2012-10-31 13:47:13 +01:00
|
|
|
usbredir_set_pipeline(dev, usb_ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
static void usbredir_ep_info(void *priv,
|
|
|
|
struct usb_redir_ep_info_header *ep_info)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
|
|
dev->endpoint[i].type = ep_info->type[i];
|
|
|
|
dev->endpoint[i].interval = ep_info->interval[i];
|
|
|
|
dev->endpoint[i].interface = ep_info->interface[i];
|
2012-10-31 13:47:13 +01:00
|
|
|
if (usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_ep_info_max_packet_size)) {
|
|
|
|
dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
|
|
|
|
}
|
2013-11-19 14:37:03 +01:00
|
|
|
#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
|
2012-01-10 14:13:05 +01:00
|
|
|
switch (dev->endpoint[i].type) {
|
|
|
|
case usb_redir_type_invalid:
|
|
|
|
break;
|
|
|
|
case usb_redir_type_iso:
|
2012-10-31 13:47:15 +01:00
|
|
|
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
|
2012-10-31 13:47:16 +01:00
|
|
|
usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
|
2012-10-31 13:47:15 +01:00
|
|
|
/* Fall through */
|
2012-01-10 14:13:05 +01:00
|
|
|
case usb_redir_type_interrupt:
|
2012-10-31 13:47:15 +01:00
|
|
|
if (!usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_ep_info_max_packet_size) ||
|
|
|
|
ep_info->max_packet_size[i] > 64) {
|
|
|
|
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
|
|
|
|
}
|
2012-10-31 13:47:16 +01:00
|
|
|
if (!usbredirparser_peer_has_cap(dev->parser,
|
|
|
|
usb_redir_cap_ep_info_max_packet_size) ||
|
|
|
|
ep_info->max_packet_size[i] > 1024) {
|
|
|
|
usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
|
|
|
|
}
|
2012-01-10 14:13:05 +01:00
|
|
|
if (dev->endpoint[i].interval == 0) {
|
|
|
|
ERROR("Received 0 interval for isoc or irq endpoint\n");
|
2012-10-31 13:47:14 +01:00
|
|
|
usbredir_reject_device(dev);
|
|
|
|
return;
|
2012-01-10 14:13:05 +01:00
|
|
|
}
|
|
|
|
/* Fall through */
|
|
|
|
case usb_redir_type_control:
|
|
|
|
case usb_redir_type_bulk:
|
2011-07-19 11:04:10 +02:00
|
|
|
DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
|
|
|
|
dev->endpoint[i].type, dev->endpoint[i].interface);
|
2012-01-10 14:13:05 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERROR("Received invalid endpoint type\n");
|
2012-10-31 13:47:14 +01:00
|
|
|
usbredir_reject_device(dev);
|
2012-03-02 21:27:08 +01:00
|
|
|
return;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
2012-10-31 13:47:15 +01:00
|
|
|
/* The new ep info may have caused a speed incompatibility, recheck */
|
|
|
|
if (dev->dev.attached &&
|
|
|
|
!(dev->dev.port->speedmask & dev->dev.speedmask)) {
|
|
|
|
ERROR("Device no longer matches speed after endpoint info change, "
|
|
|
|
"disconnecting!\n");
|
|
|
|
usbredir_reject_device(dev);
|
|
|
|
return;
|
|
|
|
}
|
2012-10-31 13:47:13 +01:00
|
|
|
usbredir_setup_usb_eps(dev);
|
2012-12-19 15:08:33 +01:00
|
|
|
usbredir_check_bulk_receiving(dev);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_configuration_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_configuration_status_header *config_status)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
2012-08-28 11:30:13 +02:00
|
|
|
USBPacket *p;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("set config status %d config %d id %"PRIu64"\n",
|
|
|
|
config_status->status, config_status->configuration, id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
p = usbredir_find_packet_by_id(dev, 0, id);
|
|
|
|
if (p) {
|
2012-08-27 16:33:08 +02:00
|
|
|
if (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.data_buf[0] = config_status->configuration;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->actual_length = 1;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_status(dev, p, config_status->status);
|
2012-08-28 11:30:13 +02:00
|
|
|
usb_generic_async_ctrl_complete(&dev->dev, p);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_alt_setting_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_alt_setting_status_header *alt_setting_status)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
2012-08-28 11:30:13 +02:00
|
|
|
USBPacket *p;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
|
|
|
|
alt_setting_status->status, alt_setting_status->interface,
|
2011-07-19 11:04:10 +02:00
|
|
|
alt_setting_status->alt, id);
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
p = usbredir_find_packet_by_id(dev, 0, id);
|
|
|
|
if (p) {
|
2012-08-27 16:33:08 +02:00
|
|
|
if (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->dev.data_buf[0] = alt_setting_status->alt;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->actual_length = 1;
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_status(dev, p, alt_setting_status->status);
|
2012-08-28 11:30:13 +02:00
|
|
|
usb_generic_async_ctrl_complete(&dev->dev, p);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_iso_stream_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_iso_stream_status_header *iso_stream_status)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = iso_stream_status->endpoint;
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status,
|
2011-07-19 11:04:10 +02:00
|
|
|
ep, id);
|
|
|
|
|
2012-01-10 14:13:04 +01:00
|
|
|
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
|
2011-11-19 10:22:46 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
|
|
|
|
if (iso_stream_status->status == usb_redir_stall) {
|
|
|
|
DPRINTF("iso stream stopped by peer ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].iso_started = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_interrupt_receiving_status_header
|
|
|
|
*interrupt_receiving_status)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = interrupt_receiving_status->endpoint;
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n",
|
2011-07-19 11:04:10 +02:00
|
|
|
interrupt_receiving_status->status, ep, id);
|
|
|
|
|
2012-01-10 14:13:04 +01:00
|
|
|
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
|
2011-11-19 10:22:46 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
dev->endpoint[EP2I(ep)].interrupt_error =
|
|
|
|
interrupt_receiving_status->status;
|
|
|
|
if (interrupt_receiving_status->status == usb_redir_stall) {
|
|
|
|
DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].interrupt_started = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
|
|
|
|
{
|
2013-11-19 14:37:03 +01:00
|
|
|
#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
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
static void usbredir_bulk_receiving_status(void *priv, uint64_t id,
|
|
|
|
struct usb_redir_bulk_receiving_status_header *bulk_receiving_status)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = bulk_receiving_status->endpoint;
|
|
|
|
|
|
|
|
DPRINTF("bulk recv status %d ep %02X id %"PRIu64"\n",
|
|
|
|
bulk_receiving_status->status, ep, id);
|
|
|
|
|
|
|
|
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].bulk_receiving_started) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bulk_receiving_status->status == usb_redir_stall) {
|
|
|
|
DPRINTF("bulk receiving stopped by peer ep %02X\n", ep);
|
|
|
|
dev->endpoint[EP2I(ep)].bulk_receiving_started = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_control_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_control_packet_header *control_packet,
|
|
|
|
uint8_t *data, int data_len)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
2012-08-28 11:30:13 +02:00
|
|
|
USBPacket *p;
|
2011-07-19 11:04:10 +02:00
|
|
|
int len = control_packet->length;
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
|
2011-07-19 11:04:10 +02:00
|
|
|
len, id);
|
|
|
|
|
2012-10-31 13:47:16 +01:00
|
|
|
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
|
|
|
|
* to work redirected to a not superspeed capable hcd */
|
|
|
|
if (dev->dev.speed == USB_SPEED_SUPER &&
|
|
|
|
!((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) &&
|
|
|
|
control_packet->requesttype == 0x80 &&
|
|
|
|
control_packet->request == 6 &&
|
|
|
|
control_packet->value == 0x100 && control_packet->index == 0 &&
|
|
|
|
data_len >= 18 && data[7] == 9) {
|
|
|
|
data[7] = 64;
|
|
|
|
}
|
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
p = usbredir_find_packet_by_id(dev, 0, id);
|
|
|
|
if (p) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_status(dev, p, control_packet->status);
|
2012-11-01 17:15:02 +01:00
|
|
|
if (data_len > 0) {
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredir_log_data(dev, "ctrl data in:", data, data_len);
|
2012-11-01 17:15:02 +01:00
|
|
|
if (data_len > sizeof(dev->dev.data_buf)) {
|
2011-07-19 11:04:10 +02:00
|
|
|
ERROR("ctrl buffer too small (%d > %zu)\n",
|
|
|
|
data_len, sizeof(dev->dev.data_buf));
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_STALL;
|
2012-11-01 17:15:02 +01:00
|
|
|
data_len = len = sizeof(dev->dev.data_buf);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2012-11-01 17:15:02 +01:00
|
|
|
memcpy(dev->dev.data_buf, data, data_len);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->actual_length = len;
|
2012-08-28 11:30:13 +02:00
|
|
|
usb_generic_async_ctrl_complete(&dev->dev, p);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_bulk_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_bulk_packet_header *bulk_packet,
|
|
|
|
uint8_t *data, int data_len)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = bulk_packet->endpoint;
|
2012-10-31 13:47:11 +01:00
|
|
|
int len = (bulk_packet->length_high << 16) | bulk_packet->length;
|
2012-08-28 11:30:13 +02:00
|
|
|
USBPacket *p;
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n",
|
|
|
|
bulk_packet->status, ep, bulk_packet->stream_id, len, id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
2012-08-28 11:30:13 +02:00
|
|
|
p = usbredir_find_packet_by_id(dev, ep, id);
|
|
|
|
if (p) {
|
2013-01-24 15:40:37 +01:00
|
|
|
size_t size = usb_packet_size(p);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
usbredir_handle_status(dev, p, bulk_packet->status);
|
2012-11-01 17:15:02 +01:00
|
|
|
if (data_len > 0) {
|
2011-07-19 11:04:10 +02:00
|
|
|
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
2012-11-01 17:15:02 +01:00
|
|
|
if (data_len > size) {
|
2012-08-28 11:33:47 +02:00
|
|
|
ERROR("bulk got more data then requested (%d > %zd)\n",
|
|
|
|
data_len, p->iov.size);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->status = USB_RET_BABBLE;
|
2012-11-01 17:15:02 +01:00
|
|
|
data_len = len = size;
|
|
|
|
}
|
2013-01-24 15:40:37 +01:00
|
|
|
usb_packet_copy(p, data, data_len);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 17:15:01 +01:00
|
|
|
p->actual_length = len;
|
2012-10-31 13:47:12 +01:00
|
|
|
if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
|
|
|
|
usb_combined_input_packet_complete(&dev->dev, p);
|
|
|
|
} else {
|
|
|
|
usb_packet_complete(&dev->dev, p);
|
|
|
|
}
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_iso_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_iso_packet_header *iso_packet,
|
|
|
|
uint8_t *data, int data_len)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = iso_packet->endpoint;
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n",
|
|
|
|
iso_packet->status, ep, data_len, id);
|
2011-07-19 11:04:10 +02:00
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
|
|
|
|
ERROR("received iso packet for non iso endpoint %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].iso_started == 0) {
|
|
|
|
DPRINTF("received iso packet for non started stream ep %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bufp_alloc also adds the packet to the ep queue */
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp_alloc(dev, data, data_len, iso_packet->status, ep, data);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
2011-07-19 11:04:10 +02:00
|
|
|
struct usb_redir_interrupt_packet_header *interrupt_packet,
|
|
|
|
uint8_t *data, int data_len)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t ep = interrupt_packet->endpoint;
|
|
|
|
|
2012-08-31 13:41:38 +02:00
|
|
|
DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n",
|
2011-07-19 11:04:10 +02:00
|
|
|
interrupt_packet->status, ep, data_len, id);
|
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
|
|
|
|
ERROR("received int packet for non interrupt endpoint %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep & USB_DIR_IN) {
|
|
|
|
if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
|
|
|
|
DPRINTF("received int packet while not started ep %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-17 12:47:14 +01:00
|
|
|
if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) {
|
2013-02-22 12:11:59 +01:00
|
|
|
usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
|
2012-11-17 12:47:14 +01:00
|
|
|
}
|
|
|
|
|
2011-07-19 11:04:10 +02:00
|
|
|
/* bufp_alloc also adds the packet to the ep queue */
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data);
|
2011-07-19 11:04:10 +02:00
|
|
|
} else {
|
usb-redir: Don't handle interrupt output packets async
Instead report them as successfully completed directly on submission, this
has 2 advantages:
1) This matches the timing of interrupt output packets on real hardware,
with the previous async handling, if an ep has an interval of say 500 ms,
then there would be 500+ ms between the submission and the guest seeing the
completion, as we wont do the write back until the qh gets polled again. And
in the mean time the guest may very well have timed out, as the guest can
reasonable expect a much quicker completion.
2) This fixes interrupt output packets potentially getting send twice
surrounding a migration. As we delay the writeback to guest memory until
the qh gets polled again, there is a window between completion and writeback
where migration can happen, in this case the destination will not know
about the completion, and it will execute the packet *again*
But it does also come with a disadvantage:
1) If the actual interrupt out to the real usb device fails, there is no
way to report this back to the guest.
This patch assumes however that interrupt outs in practice never fail, as
they are only used by specialized drivers, which are unlikely to issue illegal
requests (unlike general class drivers which often issue requests which some
devices don't implement). And that thus the advantages outway the disadvantage.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-17 12:26:57 +01:00
|
|
|
/*
|
|
|
|
* We report output interrupt packets as completed directly upon
|
|
|
|
* submission, so all we can do here if one failed is warn.
|
|
|
|
*/
|
|
|
|
if (interrupt_packet->status) {
|
|
|
|
WARNING("interrupt output failed status %d ep %02X id %"PRIu64"\n",
|
|
|
|
interrupt_packet->status, ep, id);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
static void usbredir_buffered_bulk_packet(void *priv, uint64_t id,
|
|
|
|
struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet,
|
|
|
|
uint8_t *data, int data_len)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t status, ep = buffered_bulk_packet->endpoint;
|
|
|
|
void *free_on_destroy;
|
|
|
|
int i, len;
|
|
|
|
|
|
|
|
DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n",
|
|
|
|
buffered_bulk_packet->status, ep, data_len, id);
|
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_BULK) {
|
|
|
|
ERROR("received buffered-bulk packet for non bulk ep %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].bulk_receiving_started == 0) {
|
|
|
|
DPRINTF("received buffered-bulk packet on not started ep %02X\n", ep);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */
|
|
|
|
len = dev->endpoint[EP2I(ep)].max_packet_size;
|
|
|
|
status = usb_redir_success;
|
|
|
|
free_on_destroy = NULL;
|
|
|
|
for (i = 0; i < data_len; i += len) {
|
|
|
|
if (len >= (data_len - i)) {
|
|
|
|
len = data_len - i;
|
|
|
|
status = buffered_bulk_packet->status;
|
|
|
|
free_on_destroy = data;
|
|
|
|
}
|
|
|
|
/* bufp_alloc also adds the packet to the ep queue */
|
|
|
|
bufp_alloc(dev, data + i, len, status, ep, free_on_destroy);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->endpoint[EP2I(ep)].pending_async_packet) {
|
|
|
|
USBPacket *p = dev->endpoint[EP2I(ep)].pending_async_packet;
|
|
|
|
dev->endpoint[EP2I(ep)].pending_async_packet = NULL;
|
|
|
|
usbredir_buffered_bulk_in_complete(dev, p, ep);
|
|
|
|
usb_packet_complete(&dev->dev, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
/*
|
|
|
|
* Migration code
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void usbredir_pre_save(void *priv)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
|
|
|
usbredir_fill_already_in_flight(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_post_load(void *priv, int version_id)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
|
2013-03-15 11:52:37 +01:00
|
|
|
if (dev->parser == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
switch (dev->device_info.speed) {
|
|
|
|
case usb_redir_speed_low:
|
|
|
|
dev->dev.speed = USB_SPEED_LOW;
|
|
|
|
break;
|
|
|
|
case usb_redir_speed_full:
|
|
|
|
dev->dev.speed = USB_SPEED_FULL;
|
|
|
|
break;
|
|
|
|
case usb_redir_speed_high:
|
|
|
|
dev->dev.speed = USB_SPEED_HIGH;
|
|
|
|
break;
|
|
|
|
case usb_redir_speed_super:
|
|
|
|
dev->dev.speed = USB_SPEED_SUPER;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev->dev.speed = USB_SPEED_FULL;
|
|
|
|
}
|
|
|
|
dev->dev.speedmask = (1 << dev->dev.speed);
|
|
|
|
|
2012-10-31 13:47:13 +01:00
|
|
|
usbredir_setup_usb_eps(dev);
|
2012-12-19 15:08:33 +01:00
|
|
|
usbredir_check_bulk_receiving(dev);
|
2012-10-31 13:47:13 +01:00
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For usbredirparser migration */
|
|
|
|
static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t *data;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (dev->parser == NULL) {
|
|
|
|
qemu_put_be32(f, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbredirparser_serialize(dev->parser, &data, &len);
|
|
|
|
qemu_oom_check(data);
|
|
|
|
|
|
|
|
qemu_put_be32(f, len);
|
|
|
|
qemu_put_buffer(f, data, len);
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
USBRedirDevice *dev = priv;
|
|
|
|
uint8_t *data;
|
|
|
|
int len, ret;
|
|
|
|
|
|
|
|
len = qemu_get_be32(f);
|
|
|
|
if (len == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-09-27 17:25:45 +02:00
|
|
|
* If our chardev is not open already at this point the usbredir connection
|
|
|
|
* has been broken (non seamless migration, or restore from disk).
|
|
|
|
*
|
|
|
|
* In this case create a temporary parser to receive the migration data,
|
|
|
|
* and schedule the close_bh to report the device as disconnected to the
|
|
|
|
* guest and to destroy the parser again.
|
2012-09-12 15:08:37 +02:00
|
|
|
*/
|
|
|
|
if (dev->parser == NULL) {
|
2012-09-27 17:25:45 +02:00
|
|
|
WARNING("usb-redir connection broken during migration\n");
|
|
|
|
usbredir_create_parser(dev);
|
|
|
|
qemu_bh_schedule(dev->chardev_close_bh);
|
2012-09-12 15:08:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
data = g_malloc(len);
|
|
|
|
qemu_get_buffer(f, data, len);
|
|
|
|
|
|
|
|
ret = usbredirparser_unserialize(dev->parser, data, len);
|
|
|
|
|
|
|
|
g_free(data);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateInfo usbredir_parser_vmstate_info = {
|
|
|
|
.name = "usb-redir-parser",
|
|
|
|
.put = usbredir_put_parser,
|
|
|
|
.get = usbredir_get_parser,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* For buffered packets (iso/irq) queue migration */
|
|
|
|
static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
struct endp_data *endp = priv;
|
2012-12-14 14:35:46 +01:00
|
|
|
USBRedirDevice *dev = endp->dev;
|
2012-09-12 15:08:37 +02:00
|
|
|
struct buf_packet *bufp;
|
2012-12-19 15:08:33 +01:00
|
|
|
int len, i = 0;
|
2012-09-12 15:08:37 +02:00
|
|
|
|
|
|
|
qemu_put_be32(f, endp->bufpq_size);
|
|
|
|
QTAILQ_FOREACH(bufp, &endp->bufpq, next) {
|
2012-12-19 15:08:33 +01:00
|
|
|
len = bufp->len - bufp->offset;
|
2012-12-14 14:35:46 +01:00
|
|
|
DPRINTF("put_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size,
|
2012-12-19 15:08:33 +01:00
|
|
|
len, bufp->status);
|
|
|
|
qemu_put_be32(f, len);
|
2012-09-12 15:08:37 +02:00
|
|
|
qemu_put_be32(f, bufp->status);
|
2012-12-19 15:08:33 +01:00
|
|
|
qemu_put_buffer(f, bufp->data + bufp->offset, len);
|
2012-12-14 14:35:46 +01:00
|
|
|
i++;
|
2012-09-12 15:08:37 +02:00
|
|
|
}
|
2012-12-14 14:35:46 +01:00
|
|
|
assert(i == endp->bufpq_size);
|
2012-09-12 15:08:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
struct endp_data *endp = priv;
|
2012-12-14 14:35:46 +01:00
|
|
|
USBRedirDevice *dev = endp->dev;
|
2012-09-12 15:08:37 +02:00
|
|
|
struct buf_packet *bufp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
endp->bufpq_size = qemu_get_be32(f);
|
|
|
|
for (i = 0; i < endp->bufpq_size; i++) {
|
2015-10-29 16:55:22 +01:00
|
|
|
bufp = g_new(struct buf_packet, 1);
|
2012-09-12 15:08:37 +02:00
|
|
|
bufp->len = qemu_get_be32(f);
|
|
|
|
bufp->status = qemu_get_be32(f);
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp->offset = 0;
|
2012-09-12 15:08:37 +02:00
|
|
|
bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
|
2012-12-19 15:08:33 +01:00
|
|
|
bufp->free_on_destroy = bufp->data;
|
2012-09-12 15:08:37 +02:00
|
|
|
qemu_get_buffer(f, bufp->data, bufp->len);
|
|
|
|
QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
|
2012-12-14 14:35:46 +01:00
|
|
|
DPRINTF("get_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size,
|
|
|
|
bufp->len, bufp->status);
|
2012-09-12 15:08:37 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
|
|
|
|
.name = "usb-redir-bufpq",
|
|
|
|
.put = usbredir_put_bufpq,
|
|
|
|
.get = usbredir_get_bufpq,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* For endp_data migration */
|
2014-09-23 14:09:54 +02:00
|
|
|
static bool usbredir_bulk_receiving_needed(void *priv)
|
|
|
|
{
|
|
|
|
struct endp_data *endp = priv;
|
|
|
|
|
|
|
|
return endp->bulk_receiving_started;
|
|
|
|
}
|
|
|
|
|
2012-12-19 15:08:33 +01:00
|
|
|
static const VMStateDescription usbredir_bulk_receiving_vmstate = {
|
|
|
|
.name = "usb-redir-ep/bulk-receiving",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2014-09-23 14:09:54 +02:00
|
|
|
.needed = usbredir_bulk_receiving_needed,
|
2012-12-19 15:08:33 +01:00
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT8(bulk_receiving_started, struct endp_data),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-23 14:09:54 +02:00
|
|
|
static bool usbredir_stream_needed(void *priv)
|
2012-12-19 15:08:33 +01:00
|
|
|
{
|
|
|
|
struct endp_data *endp = priv;
|
|
|
|
|
2014-09-23 14:09:54 +02:00
|
|
|
return endp->max_streams;
|
2012-12-19 15:08:33 +01:00
|
|
|
}
|
|
|
|
|
2013-11-19 14:37:03 +01:00
|
|
|
static const VMStateDescription usbredir_stream_vmstate = {
|
|
|
|
.name = "usb-redir-ep/stream-state",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2014-09-23 14:09:54 +02:00
|
|
|
.needed = usbredir_stream_needed,
|
2013-11-19 14:37:03 +01:00
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT32(max_streams, struct endp_data),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-09-12 15:08:37 +02:00
|
|
|
static const VMStateDescription usbredir_ep_vmstate = {
|
|
|
|
.name = "usb-redir-ep",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT8(type, struct endp_data),
|
|
|
|
VMSTATE_UINT8(interval, struct endp_data),
|
|
|
|
VMSTATE_UINT8(interface, struct endp_data),
|
|
|
|
VMSTATE_UINT16(max_packet_size, struct endp_data),
|
|
|
|
VMSTATE_UINT8(iso_started, struct endp_data),
|
|
|
|
VMSTATE_UINT8(iso_error, struct endp_data),
|
|
|
|
VMSTATE_UINT8(interrupt_started, struct endp_data),
|
|
|
|
VMSTATE_UINT8(interrupt_error, struct endp_data),
|
|
|
|
VMSTATE_UINT8(bufpq_prefilled, struct endp_data),
|
|
|
|
VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data),
|
|
|
|
{
|
|
|
|
.name = "bufpq",
|
|
|
|
.version_id = 0,
|
|
|
|
.field_exists = NULL,
|
|
|
|
.size = 0,
|
|
|
|
.info = &usbredir_ep_bufpq_vmstate_info,
|
|
|
|
.flags = VMS_SINGLE,
|
|
|
|
.offset = 0,
|
|
|
|
},
|
|
|
|
VMSTATE_INT32(bufpq_target_size, struct endp_data),
|
|
|
|
VMSTATE_END_OF_LIST()
|
2012-12-19 15:08:33 +01:00
|
|
|
},
|
2014-09-23 14:09:54 +02:00
|
|
|
.subsections = (const VMStateDescription*[]) {
|
|
|
|
&usbredir_bulk_receiving_vmstate,
|
|
|
|
&usbredir_stream_vmstate,
|
|
|
|
NULL
|
2012-09-12 15:08:37 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* For PacketIdQueue migration */
|
|
|
|
static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
struct PacketIdQueue *q = priv;
|
|
|
|
USBRedirDevice *dev = q->dev;
|
|
|
|
struct PacketIdQueueEntry *e;
|
|
|
|
int remain = q->size;
|
|
|
|
|
|
|
|
DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size);
|
|
|
|
qemu_put_be32(f, q->size);
|
|
|
|
QTAILQ_FOREACH(e, &q->head, next) {
|
|
|
|
qemu_put_be64(f, e->id);
|
|
|
|
remain--;
|
|
|
|
}
|
|
|
|
assert(remain == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused)
|
|
|
|
{
|
|
|
|
struct PacketIdQueue *q = priv;
|
|
|
|
USBRedirDevice *dev = q->dev;
|
|
|
|
int i, size;
|
|
|
|
uint64_t id;
|
|
|
|
|
|
|
|
size = qemu_get_be32(f);
|
|
|
|
DPRINTF("get_packet_id_q %s size %d\n", q->name, size);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
id = qemu_get_be64(f);
|
|
|
|
packet_id_queue_add(q, id);
|
|
|
|
}
|
|
|
|
assert(q->size == size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = {
|
|
|
|
.name = "usb-redir-packet-id-q",
|
|
|
|
.put = usbredir_put_packet_id_q,
|
|
|
|
.get = usbredir_get_packet_id_q,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = {
|
|
|
|
.name = "usb-redir-packet-id-queue",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
{
|
|
|
|
.name = "queue",
|
|
|
|
.version_id = 0,
|
|
|
|
.field_exists = NULL,
|
|
|
|
.size = 0,
|
|
|
|
.info = &usbredir_ep_packet_id_q_vmstate_info,
|
|
|
|
.flags = VMS_SINGLE,
|
|
|
|
.offset = 0,
|
|
|
|
},
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* For usb_redir_device_connect_header migration */
|
|
|
|
static const VMStateDescription usbredir_device_info_vmstate = {
|
|
|
|
.name = "usb-redir-device-info",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT8(speed, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_UINT16(device_version_bcd,
|
|
|
|
struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* For usb_redir_interface_info_header migration */
|
|
|
|
static const VMStateDescription usbredir_interface_info_vmstate = {
|
|
|
|
.name = "usb-redir-interface-info",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT32(interface_count,
|
|
|
|
struct usb_redir_interface_info_header),
|
|
|
|
VMSTATE_UINT8_ARRAY(interface,
|
|
|
|
struct usb_redir_interface_info_header, 32),
|
|
|
|
VMSTATE_UINT8_ARRAY(interface_class,
|
|
|
|
struct usb_redir_interface_info_header, 32),
|
|
|
|
VMSTATE_UINT8_ARRAY(interface_subclass,
|
|
|
|
struct usb_redir_interface_info_header, 32),
|
|
|
|
VMSTATE_UINT8_ARRAY(interface_protocol,
|
|
|
|
struct usb_redir_interface_info_header, 32),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* And finally the USBRedirDevice vmstate itself */
|
|
|
|
static const VMStateDescription usbredir_vmstate = {
|
|
|
|
.name = "usb-redir",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.pre_save = usbredir_pre_save,
|
|
|
|
.post_load = usbredir_post_load,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_USB_DEVICE(dev, USBRedirDevice),
|
2015-01-08 10:18:59 +01:00
|
|
|
VMSTATE_TIMER_PTR(attach_timer, USBRedirDevice),
|
2012-09-12 15:08:37 +02:00
|
|
|
{
|
|
|
|
.name = "parser",
|
|
|
|
.version_id = 0,
|
|
|
|
.field_exists = NULL,
|
|
|
|
.size = 0,
|
|
|
|
.info = &usbredir_parser_vmstate_info,
|
|
|
|
.flags = VMS_SINGLE,
|
|
|
|
.offset = 0,
|
|
|
|
},
|
|
|
|
VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1,
|
|
|
|
usbredir_ep_vmstate, struct endp_data),
|
|
|
|
VMSTATE_STRUCT(cancelled, USBRedirDevice, 1,
|
|
|
|
usbredir_ep_packet_id_queue_vmstate,
|
|
|
|
struct PacketIdQueue),
|
|
|
|
VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1,
|
|
|
|
usbredir_ep_packet_id_queue_vmstate,
|
|
|
|
struct PacketIdQueue),
|
|
|
|
VMSTATE_STRUCT(device_info, USBRedirDevice, 1,
|
|
|
|
usbredir_device_info_vmstate,
|
|
|
|
struct usb_redir_device_connect_header),
|
|
|
|
VMSTATE_STRUCT(interface_info, USBRedirDevice, 1,
|
|
|
|
usbredir_interface_info_vmstate,
|
|
|
|
struct usb_redir_interface_info_header),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-02-04 00:39:57 +01:00
|
|
|
static Property usbredir_properties[] = {
|
|
|
|
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
|
2012-11-15 14:15:00 +01:00
|
|
|
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning),
|
2012-01-25 09:50:39 +01:00
|
|
|
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
|
2012-02-04 00:39:57 +01:00
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
2011-12-15 21:53:10 +01:00
|
|
|
static void usbredir_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
2012-02-04 00:39:57 +01:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-15 21:53:10 +01:00
|
|
|
|
2014-09-19 08:48:39 +02:00
|
|
|
uc->realize = usbredir_realize;
|
2011-12-15 21:53:10 +01:00
|
|
|
uc->product_desc = "USB Redirection Device";
|
|
|
|
uc->handle_destroy = usbredir_handle_destroy;
|
|
|
|
uc->cancel_packet = usbredir_cancel_packet;
|
|
|
|
uc->handle_reset = usbredir_handle_reset;
|
|
|
|
uc->handle_data = usbredir_handle_data;
|
|
|
|
uc->handle_control = usbredir_handle_control;
|
2012-10-31 13:47:12 +01:00
|
|
|
uc->flush_ep_queue = usbredir_flush_ep_queue;
|
2012-12-14 14:35:43 +01:00
|
|
|
uc->ep_stopped = usbredir_ep_stopped;
|
2013-11-19 14:37:03 +01:00
|
|
|
uc->alloc_streams = usbredir_alloc_streams;
|
|
|
|
uc->free_streams = usbredir_free_streams;
|
2012-09-12 15:08:37 +02:00
|
|
|
dc->vmsd = &usbredir_vmstate;
|
2012-02-04 00:39:57 +01:00
|
|
|
dc->props = usbredir_properties;
|
2013-07-29 16:17:45 +02:00
|
|
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
2011-12-15 21:53:10 +01:00
|
|
|
}
|
|
|
|
|
2014-10-07 10:00:26 +02:00
|
|
|
static void usbredir_instance_init(Object *obj)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(obj);
|
2015-05-06 14:55:35 +02:00
|
|
|
USBRedirDevice *dev = USB_REDIRECT(udev);
|
2014-10-07 10:00:26 +02:00
|
|
|
|
|
|
|
device_add_bootindex_property(obj, &dev->bootindex,
|
|
|
|
"bootindex", NULL,
|
|
|
|
&udev->qdev, NULL);
|
|
|
|
}
|
|
|
|
|
2013-01-10 16:19:07 +01:00
|
|
|
static const TypeInfo usbredir_dev_info = {
|
2015-05-06 14:55:35 +02:00
|
|
|
.name = TYPE_USB_REDIR,
|
2012-02-04 00:39:57 +01:00
|
|
|
.parent = TYPE_USB_DEVICE,
|
|
|
|
.instance_size = sizeof(USBRedirDevice),
|
|
|
|
.class_init = usbredir_class_initfn,
|
2014-10-07 10:00:26 +02:00
|
|
|
.instance_init = usbredir_instance_init,
|
2011-07-19 11:04:10 +02:00
|
|
|
};
|
|
|
|
|
2012-02-09 15:20:55 +01:00
|
|
|
static void usbredir_register_types(void)
|
2011-07-19 11:04:10 +02:00
|
|
|
{
|
2012-02-04 00:39:57 +01:00
|
|
|
type_register_static(&usbredir_dev_info);
|
2011-07-19 11:04:10 +02:00
|
|
|
}
|
2012-02-09 15:20:55 +01:00
|
|
|
|
|
|
|
type_init(usbredir_register_types)
|