|
Packit |
9795e1 |
/* usbredirhost.c usb network redirection usb host code.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
Copyright 2010-2012 Red Hat, Inc.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
Red Hat Authors:
|
|
Packit |
9795e1 |
Hans de Goede <hdegoede@redhat.com>
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
This library is free software; you can redistribute it and/or
|
|
Packit |
9795e1 |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
9795e1 |
License as published by the Free Software Foundation; either
|
|
Packit |
9795e1 |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
This library is distributed in the hope that it will be useful,
|
|
Packit |
9795e1 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
9795e1 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
9795e1 |
Lesser General Public License for more details.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
9795e1 |
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
#include "config.h"
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#include <stdio.h>
|
|
Packit |
9795e1 |
#include <stdlib.h>
|
|
Packit |
9795e1 |
#include <stdarg.h>
|
|
Packit |
9795e1 |
#include <stdbool.h>
|
|
Packit |
9795e1 |
#include <string.h>
|
|
Packit |
9795e1 |
#include <errno.h>
|
|
Packit |
9795e1 |
#include <unistd.h>
|
|
Packit |
9795e1 |
#include <inttypes.h>
|
|
Packit |
9795e1 |
#include "usbredirhost.h"
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#define MAX_ENDPOINTS 32
|
|
Packit |
9795e1 |
#define MAX_INTERFACES 32 /* Max 32 endpoints and thus interfaces */
|
|
Packit |
9795e1 |
#define CTRL_TIMEOUT 5000 /* USB specifies a 5 second max timeout */
|
|
Packit |
9795e1 |
#define BULK_TIMEOUT 0 /* No timeout for bulk transfers */
|
|
Packit |
9795e1 |
#define ISO_TIMEOUT 1000
|
|
Packit |
9795e1 |
#define INTERRUPT_TIMEOUT 0 /* No timeout for interrupt transfers */
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#define MAX_TRANSFER_COUNT 16
|
|
Packit |
9795e1 |
#define MAX_PACKETS_PER_TRANSFER 32
|
|
Packit |
9795e1 |
#define INTERRUPT_TRANSFER_COUNT 5
|
|
Packit |
9795e1 |
/* Special packet_idx value indicating a submitted transfer */
|
|
Packit |
9795e1 |
#define SUBMITTED_IDX -1
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* quirk flags */
|
|
Packit |
9795e1 |
#define QUIRK_DO_NOT_RESET 0x01
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Macros to go from an endpoint address to an index for our ep array */
|
|
Packit |
9795e1 |
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
|
|
Packit |
9795e1 |
#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Locking convenience macros */
|
|
Packit |
9795e1 |
#define LOCK(host) \
|
|
Packit |
9795e1 |
do { \
|
|
Packit |
9795e1 |
if ((host)->lock) \
|
|
Packit |
9795e1 |
(host)->parser->lock_func((host)->lock); \
|
|
Packit |
9795e1 |
} while (0)
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#define UNLOCK(host) \
|
|
Packit |
9795e1 |
do { \
|
|
Packit |
9795e1 |
if ((host)->lock) \
|
|
Packit |
9795e1 |
(host)->parser->unlock_func((host)->lock); \
|
|
Packit |
9795e1 |
} while (0)
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#define FLUSH(host) \
|
|
Packit |
9795e1 |
do { \
|
|
Packit |
9795e1 |
if ((host)->flush_writes_func) \
|
|
Packit |
9795e1 |
(host)->flush_writes_func((host)->func_priv); \
|
|
Packit |
9795e1 |
} while (0)
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirtransfer {
|
|
Packit |
9795e1 |
struct usbredirhost *host; /* Back pointer to the the redirhost */
|
|
Packit |
9795e1 |
struct libusb_transfer *transfer; /* Back pointer to the libusb transfer */
|
|
Packit |
9795e1 |
uint64_t id;
|
|
Packit |
9795e1 |
uint8_t cancelled;
|
|
Packit |
9795e1 |
int packet_idx;
|
|
Packit |
9795e1 |
union {
|
|
Packit |
9795e1 |
struct usb_redir_control_packet_header control_packet;
|
|
Packit |
9795e1 |
struct usb_redir_bulk_packet_header bulk_packet;
|
|
Packit |
9795e1 |
struct usb_redir_iso_packet_header iso_packet;
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header interrupt_packet;
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
struct usbredirtransfer *next;
|
|
Packit |
9795e1 |
struct usbredirtransfer *prev;
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirhost_ep {
|
|
Packit |
9795e1 |
uint8_t type;
|
|
Packit |
9795e1 |
uint8_t interval;
|
|
Packit |
9795e1 |
uint8_t interface;
|
|
Packit |
9795e1 |
uint8_t warn_on_drop;
|
|
Packit |
9795e1 |
uint8_t stream_started;
|
|
Packit |
9795e1 |
uint8_t pkts_per_transfer;
|
|
Packit |
9795e1 |
uint8_t transfer_count;
|
|
Packit |
9795e1 |
int out_idx;
|
|
Packit |
9795e1 |
int drop_packets;
|
|
Packit |
9795e1 |
int max_packetsize;
|
|
Packit |
9795e1 |
unsigned int max_streams;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer[MAX_TRANSFER_COUNT];
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirhost {
|
|
Packit |
9795e1 |
struct usbredirparser *parser;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void *lock;
|
|
Packit |
9795e1 |
void *disconnect_lock;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirparser_log log_func;
|
|
Packit |
9795e1 |
usbredirparser_read read_func;
|
|
Packit |
9795e1 |
usbredirparser_write write_func;
|
|
Packit |
9795e1 |
usbredirhost_flush_writes flush_writes_func;
|
|
Packit |
9795e1 |
usbredirhost_buffered_output_size buffered_output_size_func;
|
|
Packit |
9795e1 |
void *func_priv;
|
|
Packit |
9795e1 |
int verbose;
|
|
Packit |
9795e1 |
libusb_context *ctx;
|
|
Packit |
9795e1 |
libusb_device *dev;
|
|
Packit |
9795e1 |
libusb_device_handle *handle;
|
|
Packit |
9795e1 |
struct libusb_device_descriptor desc;
|
|
Packit |
9795e1 |
struct libusb_config_descriptor *config;
|
|
Packit |
9795e1 |
int quirks;
|
|
Packit |
9795e1 |
int restore_config;
|
|
Packit |
9795e1 |
int claimed;
|
|
Packit |
9795e1 |
int reset;
|
|
Packit |
9795e1 |
int disconnected;
|
|
Packit |
9795e1 |
int read_status;
|
|
Packit |
9795e1 |
int cancels_pending;
|
|
Packit |
9795e1 |
int wait_disconnect;
|
|
Packit |
9795e1 |
int connect_pending;
|
|
Packit |
9795e1 |
struct usbredirhost_ep endpoint[MAX_ENDPOINTS];
|
|
Packit |
9795e1 |
uint8_t alt_setting[MAX_INTERFACES];
|
|
Packit |
9795e1 |
struct usbredirtransfer transfers_head;
|
|
Packit |
9795e1 |
struct usbredirfilter_rule *filter_rules;
|
|
Packit |
9795e1 |
int filter_rules_count;
|
|
Packit |
9795e1 |
struct {
|
|
Packit |
9795e1 |
uint64_t higher;
|
|
Packit |
9795e1 |
uint64_t lower;
|
|
Packit |
9795e1 |
bool dropping;
|
|
Packit |
9795e1 |
} iso_threshold;
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirhost_dev_ids {
|
|
Packit |
9795e1 |
int vendor_id;
|
|
Packit |
9795e1 |
int product_id;
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static const struct usbredirhost_dev_ids usbredirhost_reset_blacklist[] = {
|
|
Packit |
9795e1 |
{ 0x1210, 0x001c },
|
|
Packit |
9795e1 |
{ 0x2798, 0x0001 },
|
|
Packit |
9795e1 |
{ -1, -1 } /* Terminating Entry */
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void
|
|
Packit |
9795e1 |
#if defined __GNUC__
|
|
Packit |
9795e1 |
__attribute__((format(printf, 3, 4)))
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
va_log(struct usbredirhost *host, int level, const char *fmt, ...)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
char buf[512];
|
|
Packit |
9795e1 |
va_list ap;
|
|
Packit |
9795e1 |
int n;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (level > host->verbose) {
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
n = sprintf(buf, "usbredirhost: ");
|
|
Packit |
9795e1 |
va_start(ap, fmt);
|
|
Packit |
9795e1 |
vsnprintf(buf + n, sizeof(buf) - n, fmt, ap);
|
|
Packit |
9795e1 |
va_end(ap);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->log_func(host->func_priv, level, buf);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#ifdef ERROR /* defined on WIN32 */
|
|
Packit |
9795e1 |
#undef ERROR
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
#define ERROR(...) va_log(host, usbredirparser_error, __VA_ARGS__)
|
|
Packit |
9795e1 |
#define WARNING(...) va_log(host, usbredirparser_warning, __VA_ARGS__)
|
|
Packit |
9795e1 |
#define INFO(...) va_log(host, usbredirparser_info, __VA_ARGS__)
|
|
Packit |
9795e1 |
#define DEBUG(...) va_log(host, usbredirparser_debug, __VA_ARGS__)
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_hello(void *priv, struct usb_redir_hello_header *h);
|
|
Packit |
9795e1 |
static void usbredirhost_reset(void *priv);
|
|
Packit |
9795e1 |
static void usbredirhost_set_configuration(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_set_configuration_header *set_configuration);
|
|
Packit |
9795e1 |
static void usbredirhost_get_configuration(void *priv, uint64_t id);
|
|
Packit |
9795e1 |
static void usbredirhost_set_alt_setting(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_set_alt_setting_header *set_alt_setting);
|
|
Packit |
9795e1 |
static void usbredirhost_get_alt_setting(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_get_alt_setting_header *get_alt_setting);
|
|
Packit |
9795e1 |
static void usbredirhost_start_iso_stream(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_iso_stream_header *start_iso_stream);
|
|
Packit |
9795e1 |
static void usbredirhost_stop_iso_stream(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_iso_stream_header *stop_iso_stream);
|
|
Packit |
9795e1 |
static void usbredirhost_start_interrupt_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving);
|
|
Packit |
9795e1 |
static void usbredirhost_stop_interrupt_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving);
|
|
Packit |
9795e1 |
static void usbredirhost_alloc_bulk_streams(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams);
|
|
Packit |
9795e1 |
static void usbredirhost_free_bulk_streams(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_free_bulk_streams_header *free_bulk_streams);
|
|
Packit |
9795e1 |
static void usbredirhost_cancel_data_packet(void *priv, uint64_t id);
|
|
Packit |
9795e1 |
static void usbredirhost_filter_reject(void *priv);
|
|
Packit |
9795e1 |
static void usbredirhost_filter_filter(void *priv,
|
|
Packit |
9795e1 |
struct usbredirfilter_rule *rules, int rules_count);
|
|
Packit |
9795e1 |
static void usbredirhost_device_disconnect_ack(void *priv);
|
|
Packit |
9795e1 |
static void usbredirhost_start_bulk_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_bulk_receiving_header *start_bulk_receiving);
|
|
Packit |
9795e1 |
static void usbredirhost_stop_bulk_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving);
|
|
Packit |
9795e1 |
static void usbredirhost_control_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_control_packet_header *control_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len);
|
|
Packit |
9795e1 |
static void usbredirhost_bulk_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_bulk_packet_header *bulk_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len);
|
|
Packit |
9795e1 |
static void usbredirhost_iso_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_iso_packet_header *iso_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len);
|
|
Packit |
9795e1 |
static void usbredirhost_interrupt_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header *interrupt_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_iso_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer);
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_buffered_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer);
|
|
Packit |
9795e1 |
static int usbredirhost_cancel_pending_urbs(struct usbredirhost *host,
|
|
Packit |
9795e1 |
int notify_guest);
|
|
Packit |
9795e1 |
static void usbredirhost_wait_for_cancel_completion(struct usbredirhost *host);
|
|
Packit |
9795e1 |
static void usbredirhost_clear_device(struct usbredirhost *host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_log(void *priv, int level, const char *msg)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->log_func(host->func_priv, level, msg);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static int usbredirhost_read(void *priv, uint8_t *data, int count)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->read_status) {
|
|
Packit |
9795e1 |
int ret = host->read_status;
|
|
Packit |
9795e1 |
host->read_status = 0;
|
|
Packit |
9795e1 |
return ret;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return host->read_func(host->func_priv, data, count);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static int usbredirhost_write(void *priv, uint8_t *data, int count)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return host->write_func(host->func_priv, data, count);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Can be called both from parser read callbacks as well as from libusb
|
|
Packit |
9795e1 |
packet completion callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_handle_disconnect(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
/* Disconnect uses its own lock to avoid needing nesting capable locks */
|
|
Packit |
9795e1 |
if (host->disconnect_lock) {
|
|
Packit |
9795e1 |
host->parser->lock_func(host->disconnect_lock);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (!host->disconnected) {
|
|
Packit |
9795e1 |
INFO("device disconnected");
|
|
Packit |
9795e1 |
usbredirparser_send_device_disconnect(host->parser);
|
|
Packit |
9795e1 |
if (usbredirparser_peer_has_cap(host->parser,
|
|
Packit |
9795e1 |
usb_redir_cap_device_disconnect_ack))
|
|
Packit |
9795e1 |
host->wait_disconnect = 1;
|
|
Packit |
9795e1 |
host->disconnected = 1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (host->disconnect_lock) {
|
|
Packit |
9795e1 |
host->parser->unlock_func(host->disconnect_lock);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* One function to convert either a transfer status code, or a libusb error
|
|
Packit |
9795e1 |
code to a usb_redir status. We handle both in one conversion function so
|
|
Packit |
9795e1 |
that we can pass error codes as status codes to the completion handler
|
|
Packit |
9795e1 |
in case of submission error (the codes don't overlap), using the completion
|
|
Packit |
9795e1 |
handler to report back the status and cleanup as it would on completion of
|
|
Packit |
9795e1 |
a successfully submitted transfer. */
|
|
Packit |
9795e1 |
static int libusb_status_or_error_to_redir_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
int status)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
switch (status) {
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_COMPLETED:
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_ERROR:
|
|
Packit |
9795e1 |
return usb_redir_ioerror;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TIMED_OUT:
|
|
Packit |
9795e1 |
return usb_redir_timeout;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_CANCELLED:
|
|
Packit |
9795e1 |
return usb_redir_cancelled;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_STALL:
|
|
Packit |
9795e1 |
return usb_redir_stall;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_NO_DEVICE:
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
return usb_redir_ioerror;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_OVERFLOW:
|
|
Packit |
9795e1 |
return usb_redir_babble;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
case LIBUSB_ERROR_INVALID_PARAM:
|
|
Packit |
9795e1 |
return usb_redir_inval;
|
|
Packit |
9795e1 |
case LIBUSB_ERROR_NO_DEVICE:
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
return usb_redir_ioerror;
|
|
Packit |
9795e1 |
case LIBUSB_ERROR_TIMEOUT:
|
|
Packit |
9795e1 |
return usb_redir_timeout;
|
|
Packit |
9795e1 |
default:
|
|
Packit |
9795e1 |
return usb_redir_ioerror;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_set_max_packetsize(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint8_t ep, uint16_t wMaxPacketSize)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int maxp, mult = 1;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
maxp = wMaxPacketSize & 0x7ff;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (libusb_get_device_speed(host->dev) == LIBUSB_SPEED_HIGH &&
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].type == usb_redir_type_iso) {
|
|
Packit |
9795e1 |
switch ((wMaxPacketSize >> 11) & 3) {
|
|
Packit |
9795e1 |
case 1: mult = 2; break;
|
|
Packit |
9795e1 |
case 2: mult = 3; break;
|
|
Packit |
9795e1 |
default: mult = 1; break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].max_packetsize = maxp * mult;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_set_max_streams(struct usbredirhost *host,
|
|
Packit |
9795e1 |
const struct libusb_endpoint_descriptor *endp)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000102
|
|
Packit |
9795e1 |
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
|
|
Packit |
9795e1 |
int max_streams, i = EP2I(endp->bEndpointAddress);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->endpoint[i].max_streams = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[i].type == usb_redir_type_bulk &&
|
|
Packit |
9795e1 |
libusb_get_ss_endpoint_companion_descriptor(host->ctx, endp,
|
|
Packit |
9795e1 |
&endp_ss_comp) == LIBUSB_SUCCESS) {
|
|
Packit |
9795e1 |
max_streams = endp_ss_comp->bmAttributes & 0x1f;
|
|
Packit |
9795e1 |
if (max_streams)
|
|
Packit |
9795e1 |
host->endpoint[i].max_streams = 1 << max_streams;
|
|
Packit |
9795e1 |
libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from open/close and parser read callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_send_interface_n_ep_info(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
const struct libusb_interface_descriptor *intf_desc;
|
|
Packit |
9795e1 |
struct usb_redir_ep_info_header ep_info;
|
|
Packit |
9795e1 |
struct usb_redir_interface_info_header interface_info = { 0, };
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->config)
|
|
Packit |
9795e1 |
interface_info.interface_count = host->config->bNumInterfaces;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < interface_info.interface_count; i++) {
|
|
Packit |
9795e1 |
intf_desc =
|
|
Packit |
9795e1 |
&host->config->interface[i].altsetting[host->alt_setting[i]];
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
interface_info.interface[i] = intf_desc->bInterfaceNumber;
|
|
Packit |
9795e1 |
interface_info.interface_class[i] = intf_desc->bInterfaceClass;
|
|
Packit |
9795e1 |
interface_info.interface_subclass[i] = intf_desc->bInterfaceSubClass;
|
|
Packit |
9795e1 |
interface_info.interface_protocol[i] = intf_desc->bInterfaceProtocol;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
usbredirparser_send_interface_info(host->parser, &interface_info);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
Packit |
9795e1 |
ep_info.type[i] = host->endpoint[i].type;
|
|
Packit |
9795e1 |
ep_info.interval[i] = host->endpoint[i].interval;
|
|
Packit |
9795e1 |
ep_info.interface[i] = host->endpoint[i].interface;
|
|
Packit |
9795e1 |
ep_info.max_packet_size[i] = host->endpoint[i].max_packetsize;
|
|
Packit |
9795e1 |
ep_info.max_streams[i] = host->endpoint[i].max_streams;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
usbredirparser_send_ep_info(host->parser, &ep_info);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from open/close and parser read callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_send_device_connect(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usb_redir_device_connect_header device_connect;
|
|
Packit |
9795e1 |
enum libusb_speed speed;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!host->disconnected) {
|
|
Packit |
9795e1 |
ERROR("internal error sending device_connect but already connected");
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!usbredirparser_have_peer_caps(host->parser) ||
|
|
Packit |
9795e1 |
host->wait_disconnect) {
|
|
Packit |
9795e1 |
host->connect_pending = 1;
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
speed = libusb_get_device_speed(host->dev);
|
|
Packit |
9795e1 |
switch (speed) {
|
|
Packit |
9795e1 |
case LIBUSB_SPEED_LOW:
|
|
Packit |
9795e1 |
device_connect.speed = usb_redir_speed_low; break;
|
|
Packit |
9795e1 |
case LIBUSB_SPEED_FULL:
|
|
Packit |
9795e1 |
device_connect.speed = usb_redir_speed_full; break;
|
|
Packit |
9795e1 |
case LIBUSB_SPEED_HIGH:
|
|
Packit |
9795e1 |
device_connect.speed = usb_redir_speed_high; break;
|
|
Packit |
9795e1 |
case LIBUSB_SPEED_SUPER:
|
|
Packit |
9795e1 |
device_connect.speed = usb_redir_speed_super; break;
|
|
Packit |
9795e1 |
default:
|
|
Packit |
9795e1 |
device_connect.speed = usb_redir_speed_unknown;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
device_connect.device_class = host->desc.bDeviceClass;
|
|
Packit |
9795e1 |
device_connect.device_subclass = host->desc.bDeviceSubClass;
|
|
Packit |
9795e1 |
device_connect.device_protocol = host->desc.bDeviceProtocol;
|
|
Packit |
9795e1 |
device_connect.vendor_id = host->desc.idVendor;
|
|
Packit |
9795e1 |
device_connect.product_id = host->desc.idProduct;
|
|
Packit |
9795e1 |
device_connect.device_version_bcd = host->desc.bcdDevice;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_send_interface_n_ep_info(host);
|
|
Packit |
9795e1 |
usbredirparser_send_device_connect(host->parser, &device_connect);
|
|
Packit |
9795e1 |
host->connect_pending = 0;
|
|
Packit |
9795e1 |
host->disconnected = 0; /* The guest may now use the device */
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from open/close and parser read callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_parse_interface(struct usbredirhost *host, int i)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int j;
|
|
Packit |
9795e1 |
const struct libusb_interface_descriptor *intf_desc;
|
|
Packit |
9795e1 |
uint8_t ep_address;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
intf_desc =
|
|
Packit |
9795e1 |
&host->config->interface[i].altsetting[host->alt_setting[i]];
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (j = 0; j < intf_desc->bNumEndpoints; j++) {
|
|
Packit |
9795e1 |
ep_address = intf_desc->endpoint[j].bEndpointAddress;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep_address)].type =
|
|
Packit |
9795e1 |
intf_desc->endpoint[j].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep_address)].interval =
|
|
Packit |
9795e1 |
intf_desc->endpoint[j].bInterval;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep_address)].interface =
|
|
Packit |
9795e1 |
intf_desc->bInterfaceNumber;
|
|
Packit |
9795e1 |
usbredirhost_set_max_packetsize(host, ep_address,
|
|
Packit |
9795e1 |
intf_desc->endpoint[j].wMaxPacketSize);
|
|
Packit |
9795e1 |
usbredirhost_set_max_streams(host, &intf_desc->endpoint[j]);
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep_address)].warn_on_drop = 1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_parse_config(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
Packit |
9795e1 |
if ((i & 0x0f) == 0) {
|
|
Packit |
9795e1 |
host->endpoint[i].type = usb_redir_type_control;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
host->endpoint[i].type = usb_redir_type_invalid;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[i].interval = 0;
|
|
Packit |
9795e1 |
host->endpoint[i].interface = 0;
|
|
Packit |
9795e1 |
host->endpoint[i].max_packetsize = 0;
|
|
Packit |
9795e1 |
host->endpoint[i].max_streams = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; host->config && i < host->config->bNumInterfaces; i++) {
|
|
Packit |
9795e1 |
usbredirhost_parse_interface(host, i);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from open/close and parser read callbacks */
|
|
Packit |
9795e1 |
static int usbredirhost_claim(struct usbredirhost *host, int initial_claim)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, n, r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->config) {
|
|
Packit |
9795e1 |
libusb_free_config_descriptor(host->config);
|
|
Packit |
9795e1 |
host->config = NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_get_device_descriptor(host->dev, &host->desc);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("could not get device descriptor: %s", libusb_error_name(r));
|
|
Packit |
9795e1 |
return libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_get_active_config_descriptor(host->dev, &host->config);
|
|
Packit |
9795e1 |
if (r < 0 && r != LIBUSB_ERROR_NOT_FOUND) {
|
|
Packit |
9795e1 |
ERROR("could not get descriptors for active configuration: %s",
|
|
Packit |
9795e1 |
libusb_error_name(r));
|
|
Packit |
9795e1 |
return libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (host->config && host->config->bNumInterfaces > MAX_INTERFACES) {
|
|
Packit |
9795e1 |
ERROR("usb decriptor has too much intefaces (%d > %d)",
|
|
Packit |
9795e1 |
(int)host->config->bNumInterfaces, MAX_INTERFACES);
|
|
Packit |
9795e1 |
return usb_redir_ioerror;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (initial_claim) {
|
|
Packit |
9795e1 |
if (host->config)
|
|
Packit |
9795e1 |
host->restore_config = host->config->bConfigurationValue;
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
host->restore_config = -1; /* unconfigured */
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* If the device is unconfigured and has only 1 config, we assume
|
|
Packit |
9795e1 |
this is the result of the user doing "safely remove hardware",
|
|
Packit |
9795e1 |
and we try to reset the device configuration to this config when
|
|
Packit |
9795e1 |
we release the device, so that it becomes usable again. */
|
|
Packit |
9795e1 |
if (host->restore_config == -1 && host->desc.bNumConfigurations == 1) {
|
|
Packit |
9795e1 |
struct libusb_config_descriptor *config;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_get_config_descriptor(host->dev, 0, &config);
|
|
Packit |
9795e1 |
if (r == 0) {
|
|
Packit |
9795e1 |
host->restore_config = config->bConfigurationValue;
|
|
Packit |
9795e1 |
libusb_free_config_descriptor(config);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* All interfaces begin at alt setting 0 when (re)claimed */
|
|
Packit |
9795e1 |
memset(host->alt_setting, 0, MAX_INTERFACES);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->claimed = 1;
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000102
|
|
Packit |
9795e1 |
libusb_set_auto_detach_kernel_driver(host->handle, 1);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
for (i = 0; host->config && i < host->config->bNumInterfaces; i++) {
|
|
Packit |
9795e1 |
n = host->config->interface[i].altsetting[0].bInterfaceNumber;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION < 0x01000102
|
|
Packit |
9795e1 |
r = libusb_detach_kernel_driver(host->handle, n);
|
|
Packit |
9795e1 |
if (r < 0 && r != LIBUSB_ERROR_NOT_FOUND
|
|
Packit |
9795e1 |
&& r != LIBUSB_ERROR_NOT_SUPPORTED) {
|
|
Packit |
9795e1 |
ERROR("could not detach driver from interface %d (configuration %d): %s",
|
|
Packit |
9795e1 |
n, host->config->bConfigurationValue, libusb_error_name(r));
|
|
Packit |
9795e1 |
return libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_claim_interface(host->handle, n);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
if (r == LIBUSB_ERROR_BUSY)
|
|
Packit |
9795e1 |
ERROR("Device is in use by another application");
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
ERROR("could not claim interface %d (configuration %d): %s",
|
|
Packit |
9795e1 |
n, host->config->bConfigurationValue,
|
|
Packit |
9795e1 |
libusb_error_name(r));
|
|
Packit |
9795e1 |
return libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_parse_config(host);
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from open/close and parser read callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_release(struct usbredirhost *host, int attach_drivers)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, n, r, current_config = -1;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!host->claimed)
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000102
|
|
Packit |
9795e1 |
/* We want to always do the attach ourselves because:
|
|
Packit |
9795e1 |
1) For compound interfaces such as usb-audio we must first release all
|
|
Packit |
9795e1 |
interfaces before we can attach the driver;
|
|
Packit |
9795e1 |
2) When releasing interfaces before calling libusb_set_configuration,
|
|
Packit |
9795e1 |
we don't want the kernel driver to get attached (our attach_drivers
|
|
Packit |
9795e1 |
parameter is 0 in this case). */
|
|
Packit |
9795e1 |
libusb_set_auto_detach_kernel_driver(host->handle, 0);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; host->config && i < host->config->bNumInterfaces; i++) {
|
|
Packit |
9795e1 |
n = host->config->interface[i].altsetting[0].bInterfaceNumber;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_release_interface(host->handle, n);
|
|
Packit |
9795e1 |
if (r < 0 && r != LIBUSB_ERROR_NOT_FOUND
|
|
Packit |
9795e1 |
&& r != LIBUSB_ERROR_NO_DEVICE) {
|
|
Packit |
9795e1 |
ERROR("could not release interface %d (configuration %d): %s",
|
|
Packit |
9795e1 |
n, host->config->bConfigurationValue, libusb_error_name(r));
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!attach_drivers)
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->claimed = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* reset the device before re-binding the kernel drivers, so that the
|
|
Packit |
9795e1 |
kernel drivers get the device in a clean state. */
|
|
Packit |
9795e1 |
if (!(host->quirks & QUIRK_DO_NOT_RESET)) {
|
|
Packit |
9795e1 |
r = libusb_reset_device(host->handle);
|
|
Packit |
9795e1 |
if (r != 0) {
|
|
Packit |
9795e1 |
/* if we're releasing the device because it was removed, resetting
|
|
Packit |
9795e1 |
* will fail. Don't print a warning in this situation */
|
|
Packit |
9795e1 |
if (r != LIBUSB_ERROR_NO_DEVICE) {
|
|
Packit |
9795e1 |
ERROR("error resetting device: %s", libusb_error_name(r));
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->config)
|
|
Packit |
9795e1 |
current_config = host->config->bConfigurationValue;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (current_config != host->restore_config) {
|
|
Packit |
9795e1 |
r = libusb_set_configuration(host->handle, host->restore_config);
|
|
Packit |
9795e1 |
if (r < 0)
|
|
Packit |
9795e1 |
ERROR("could not restore configuration to %d: %s",
|
|
Packit |
9795e1 |
host->restore_config, libusb_error_name(r));
|
|
Packit |
9795e1 |
return; /* set_config automatically binds drivers for the new config */
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; host->config && i < host->config->bNumInterfaces; i++) {
|
|
Packit |
9795e1 |
n = host->config->interface[i].altsetting[0].bInterfaceNumber;
|
|
Packit |
9795e1 |
r = libusb_attach_kernel_driver(host->handle, n);
|
|
Packit |
9795e1 |
if (r < 0 && r != LIBUSB_ERROR_NOT_FOUND /* No driver */
|
|
Packit |
9795e1 |
&& r != LIBUSB_ERROR_NO_DEVICE /* Device unplugged */
|
|
Packit |
9795e1 |
&& r != LIBUSB_ERROR_NOT_SUPPORTED /* Not supported */
|
|
Packit |
9795e1 |
&& r != LIBUSB_ERROR_BUSY /* driver rebound already */) {
|
|
Packit |
9795e1 |
ERROR("could not re-attach driver to interface %d (configuration %d): %s",
|
|
Packit |
9795e1 |
n, host->config->bConfigurationValue, libusb_error_name(r));
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirhost *usbredirhost_open(
|
|
Packit |
9795e1 |
libusb_context *usb_ctx,
|
|
Packit |
9795e1 |
libusb_device_handle *usb_dev_handle,
|
|
Packit |
9795e1 |
usbredirparser_log log_func,
|
|
Packit |
9795e1 |
usbredirparser_read read_guest_data_func,
|
|
Packit |
9795e1 |
usbredirparser_write write_guest_data_func,
|
|
Packit |
9795e1 |
void *func_priv, const char *version, int verbose, int flags)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
return usbredirhost_open_full(usb_ctx, usb_dev_handle, log_func,
|
|
Packit |
9795e1 |
read_guest_data_func, write_guest_data_func,
|
|
Packit |
9795e1 |
NULL, NULL, NULL, NULL, NULL,
|
|
Packit |
9795e1 |
func_priv, version, verbose, flags);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
struct usbredirhost *usbredirhost_open_full(
|
|
Packit |
9795e1 |
libusb_context *usb_ctx,
|
|
Packit |
9795e1 |
libusb_device_handle *usb_dev_handle,
|
|
Packit |
9795e1 |
usbredirparser_log log_func,
|
|
Packit |
9795e1 |
usbredirparser_read read_guest_data_func,
|
|
Packit |
9795e1 |
usbredirparser_write write_guest_data_func,
|
|
Packit |
9795e1 |
usbredirhost_flush_writes flush_writes_func,
|
|
Packit |
9795e1 |
usbredirparser_alloc_lock alloc_lock_func,
|
|
Packit |
9795e1 |
usbredirparser_lock lock_func,
|
|
Packit |
9795e1 |
usbredirparser_unlock unlock_func,
|
|
Packit |
9795e1 |
usbredirparser_free_lock free_lock_func,
|
|
Packit |
9795e1 |
void *func_priv, const char *version, int verbose, int flags)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host;
|
|
Packit |
9795e1 |
int parser_flags = usbredirparser_fl_usb_host;
|
|
Packit |
9795e1 |
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host = calloc(1, sizeof(*host));
|
|
Packit |
9795e1 |
if (!host) {
|
|
Packit |
9795e1 |
log_func(func_priv, usbredirparser_error,
|
|
Packit |
9795e1 |
"usbredirhost error: Out of memory allocating usbredirhost");
|
|
Packit |
9795e1 |
libusb_close(usb_dev_handle);
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->ctx = usb_ctx;
|
|
Packit |
9795e1 |
host->log_func = log_func;
|
|
Packit |
9795e1 |
host->read_func = read_guest_data_func;
|
|
Packit |
9795e1 |
host->write_func = write_guest_data_func;
|
|
Packit |
9795e1 |
host->flush_writes_func = flush_writes_func;
|
|
Packit |
9795e1 |
host->func_priv = func_priv;
|
|
Packit |
9795e1 |
host->verbose = verbose;
|
|
Packit |
9795e1 |
host->disconnected = 1; /* No device is connected initially */
|
|
Packit |
9795e1 |
host->parser = usbredirparser_create();
|
|
Packit |
9795e1 |
if (!host->parser) {
|
|
Packit |
9795e1 |
log_func(func_priv, usbredirparser_error,
|
|
Packit |
9795e1 |
"usbredirhost error: Out of memory allocating usbredirparser");
|
|
Packit |
9795e1 |
usbredirhost_close(host);
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->parser->priv = host;
|
|
Packit |
9795e1 |
host->parser->log_func = usbredirhost_log;
|
|
Packit |
9795e1 |
host->parser->read_func = usbredirhost_read;
|
|
Packit |
9795e1 |
host->parser->write_func = usbredirhost_write;
|
|
Packit |
9795e1 |
host->parser->hello_func = usbredirhost_hello;
|
|
Packit |
9795e1 |
host->parser->reset_func = usbredirhost_reset;
|
|
Packit |
9795e1 |
host->parser->set_configuration_func = usbredirhost_set_configuration;
|
|
Packit |
9795e1 |
host->parser->get_configuration_func = usbredirhost_get_configuration;
|
|
Packit |
9795e1 |
host->parser->set_alt_setting_func = usbredirhost_set_alt_setting;
|
|
Packit |
9795e1 |
host->parser->get_alt_setting_func = usbredirhost_get_alt_setting;
|
|
Packit |
9795e1 |
host->parser->start_iso_stream_func = usbredirhost_start_iso_stream;
|
|
Packit |
9795e1 |
host->parser->stop_iso_stream_func = usbredirhost_stop_iso_stream;
|
|
Packit |
9795e1 |
host->parser->start_interrupt_receiving_func =
|
|
Packit |
9795e1 |
usbredirhost_start_interrupt_receiving;
|
|
Packit |
9795e1 |
host->parser->stop_interrupt_receiving_func =
|
|
Packit |
9795e1 |
usbredirhost_stop_interrupt_receiving;
|
|
Packit |
9795e1 |
host->parser->alloc_bulk_streams_func = usbredirhost_alloc_bulk_streams;
|
|
Packit |
9795e1 |
host->parser->free_bulk_streams_func = usbredirhost_free_bulk_streams;
|
|
Packit |
9795e1 |
host->parser->cancel_data_packet_func = usbredirhost_cancel_data_packet;
|
|
Packit |
9795e1 |
host->parser->filter_reject_func = usbredirhost_filter_reject;
|
|
Packit |
9795e1 |
host->parser->filter_filter_func = usbredirhost_filter_filter;
|
|
Packit |
9795e1 |
host->parser->device_disconnect_ack_func =
|
|
Packit |
9795e1 |
usbredirhost_device_disconnect_ack;
|
|
Packit |
9795e1 |
host->parser->start_bulk_receiving_func =
|
|
Packit |
9795e1 |
usbredirhost_start_bulk_receiving;
|
|
Packit |
9795e1 |
host->parser->stop_bulk_receiving_func =
|
|
Packit |
9795e1 |
usbredirhost_stop_bulk_receiving;
|
|
Packit |
9795e1 |
host->parser->control_packet_func = usbredirhost_control_packet;
|
|
Packit |
9795e1 |
host->parser->bulk_packet_func = usbredirhost_bulk_packet;
|
|
Packit |
9795e1 |
host->parser->iso_packet_func = usbredirhost_iso_packet;
|
|
Packit |
9795e1 |
host->parser->interrupt_packet_func = usbredirhost_interrupt_packet;
|
|
Packit |
9795e1 |
host->parser->alloc_lock_func = alloc_lock_func;
|
|
Packit |
9795e1 |
host->parser->lock_func = lock_func;
|
|
Packit |
9795e1 |
host->parser->unlock_func = unlock_func;
|
|
Packit |
9795e1 |
host->parser->free_lock_func = free_lock_func;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->parser->alloc_lock_func) {
|
|
Packit |
9795e1 |
host->lock = host->parser->alloc_lock_func();
|
|
Packit |
9795e1 |
host->disconnect_lock = host->parser->alloc_lock_func();
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (flags & usbredirhost_fl_write_cb_owns_buffer) {
|
|
Packit |
9795e1 |
parser_flags |= usbredirparser_fl_write_cb_owns_buffer;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_device_disconnect_ack);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirparser_init(host->parser, version, caps, USB_REDIR_CAPS_SIZE,
|
|
Packit |
9795e1 |
parser_flags);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#if LIBUSB_API_VERSION >= 0x01000106
|
|
Packit |
9795e1 |
libusb_set_option(host->ctx, LIBUSB_OPTION_LOG_LEVEL, host->verbose);
|
|
Packit |
9795e1 |
#else
|
|
Packit |
9795e1 |
libusb_set_debug(host->ctx, host->verbose);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (usbredirhost_set_device(host, usb_dev_handle) != usb_redir_success) {
|
|
Packit |
9795e1 |
usbredirhost_close(host);
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return host;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void usbredirhost_close(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
usbredirhost_clear_device(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->lock) {
|
|
Packit |
9795e1 |
host->parser->free_lock_func(host->lock);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (host->disconnect_lock) {
|
|
Packit |
9795e1 |
host->parser->free_lock_func(host->disconnect_lock);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (host->parser) {
|
|
Packit |
9795e1 |
usbredirparser_destroy(host->parser);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
free(host->filter_rules);
|
|
Packit |
9795e1 |
free(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static int usbredirhost_reset_device(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->quirks & QUIRK_DO_NOT_RESET) {
|
|
Packit |
9795e1 |
return 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_reset_device(host->handle);
|
|
Packit |
9795e1 |
if (r != 0) {
|
|
Packit |
9795e1 |
ERROR("error resetting device: %s", libusb_error_name(r));
|
|
Packit |
9795e1 |
usbredirhost_clear_device(host);
|
|
Packit |
9795e1 |
return r;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 1;
|
|
Packit |
9795e1 |
return 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirhost_set_device(struct usbredirhost *host,
|
|
Packit |
9795e1 |
libusb_device_handle *usb_dev_handle)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, r, status;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_clear_device(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!usb_dev_handle)
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->dev = libusb_get_device(usb_dev_handle);
|
|
Packit |
9795e1 |
host->handle = usb_dev_handle;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
status = usbredirhost_claim(host, 1);
|
|
Packit |
9795e1 |
if (status != usb_redir_success) {
|
|
Packit |
9795e1 |
usbredirhost_clear_device(host);
|
|
Packit |
9795e1 |
return status;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; usbredirhost_reset_blacklist[i].vendor_id != -1; i++) {
|
|
Packit |
9795e1 |
if (host->desc.idVendor == usbredirhost_reset_blacklist[i].vendor_id &&
|
|
Packit |
9795e1 |
host->desc.idProduct ==
|
|
Packit |
9795e1 |
usbredirhost_reset_blacklist[i].product_id) {
|
|
Packit |
9795e1 |
host->quirks |= QUIRK_DO_NOT_RESET;
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* The first thing almost any usb-guest does is a (slow) device-reset
|
|
Packit |
9795e1 |
so lets do that before hand */
|
|
Packit |
9795e1 |
r = usbredirhost_reset_device(host);
|
|
Packit |
9795e1 |
if (r != 0) {
|
|
Packit |
9795e1 |
return libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_send_device_connect(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_clear_device(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (!host->dev)
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (usbredirhost_cancel_pending_urbs(host, 0))
|
|
Packit |
9795e1 |
usbredirhost_wait_for_cancel_completion(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_release(host, 1);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->config) {
|
|
Packit |
9795e1 |
libusb_free_config_descriptor(host->config);
|
|
Packit |
9795e1 |
host->config = NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (host->handle) {
|
|
Packit |
9795e1 |
libusb_close(host->handle);
|
|
Packit |
9795e1 |
host->handle = NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->connect_pending = 0;
|
|
Packit |
9795e1 |
host->quirks = 0;
|
|
Packit |
9795e1 |
host->dev = NULL;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirhost_read_guest_data(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
return usbredirparser_do_read(host->parser);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirhost_has_data_to_write(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
return usbredirparser_has_data_to_write(host->parser);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirhost_write_guest_data(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
return usbredirparser_do_write(host->parser);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void usbredirhost_free_write_buffer(struct usbredirhost *host, uint8_t *data)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
usbredirparser_free_write_buffer(host->parser, data);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static struct usbredirtransfer *usbredirhost_alloc_transfer(
|
|
Packit |
9795e1 |
struct usbredirhost *host, int iso_packets)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *redir_transfer;
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
redir_transfer = calloc(1, sizeof(*redir_transfer));
|
|
Packit |
9795e1 |
libusb_transfer = libusb_alloc_transfer(iso_packets);
|
|
Packit |
9795e1 |
if (!redir_transfer || !libusb_transfer) {
|
|
Packit |
9795e1 |
ERROR("out of memory allocating usb transfer, dropping packet");
|
|
Packit |
9795e1 |
free(redir_transfer);
|
|
Packit |
9795e1 |
libusb_free_transfer(libusb_transfer);
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
redir_transfer->host = host;
|
|
Packit |
9795e1 |
redir_transfer->transfer = libusb_transfer;
|
|
Packit |
9795e1 |
libusb_transfer->user_data = redir_transfer;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return redir_transfer;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_free_transfer(struct usbredirtransfer *transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (!transfer)
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* In certain cases this should really be a usbredirparser_free_packet_data
|
|
Packit |
9795e1 |
but since we use the same malloc impl. as usbredirparser this is ok. */
|
|
Packit |
9795e1 |
free(transfer->transfer->buffer);
|
|
Packit |
9795e1 |
libusb_free_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
free(transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_add_transfer(struct usbredirhost *host,
|
|
Packit |
9795e1 |
struct usbredirtransfer *new_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = &host->transfers_head;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
while (transfer->next) {
|
|
Packit |
9795e1 |
transfer = transfer->next;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
new_transfer->prev = transfer;
|
|
Packit |
9795e1 |
transfer->next = new_transfer;
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Note caller must hold the host lock */
|
|
Packit |
9795e1 |
static void usbredirhost_remove_and_free_transfer(
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (transfer->next)
|
|
Packit |
9795e1 |
transfer->next->prev = transfer->prev;
|
|
Packit |
9795e1 |
if (transfer->prev)
|
|
Packit |
9795e1 |
transfer->prev->next = transfer->next;
|
|
Packit |
9795e1 |
usbredirhost_free_transfer(transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from both parser read and packet complete callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_cancel_stream_unlocked(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint8_t ep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < host->endpoint[EP2I(ep)].transfer_count; i++) {
|
|
Packit |
9795e1 |
transfer = host->endpoint[EP2I(ep)].transfer[i];
|
|
Packit |
9795e1 |
if (transfer->packet_idx == SUBMITTED_IDX) {
|
|
Packit |
9795e1 |
libusb_cancel_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
transfer->cancelled = 1;
|
|
Packit |
9795e1 |
host->cancels_pending++;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirhost_free_transfer(transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i] = NULL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].out_idx = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].stream_started = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].drop_packets = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].pkts_per_transfer = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer_count = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_cancel_stream(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint8_t ep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_send_stream_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep, uint8_t status)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
switch (host->endpoint[EP2I(ep)].type) {
|
|
Packit |
9795e1 |
case usb_redir_type_iso: {
|
|
Packit |
9795e1 |
struct usb_redir_iso_stream_status_header iso_status = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_iso_stream_status(host->parser, id, &iso_status);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
case usb_redir_type_bulk: {
|
|
Packit |
9795e1 |
struct usb_redir_bulk_receiving_status_header bulk_status = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_receiving_status(host->parser, id,
|
|
Packit |
9795e1 |
&bulk_status);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
case usb_redir_type_interrupt: {
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_receiving_status_header interrupt_status = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_interrupt_receiving_status(host->parser, id,
|
|
Packit |
9795e1 |
&interrupt_status);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static int usbredirhost_can_write_iso_package(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
uint64_t size;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!host->buffered_output_size_func)
|
|
Packit |
9795e1 |
return true;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
size = host->buffered_output_size_func(host->func_priv);
|
|
Packit |
9795e1 |
if (size >= host->iso_threshold.higher) {
|
|
Packit |
9795e1 |
if (!host->iso_threshold.dropping)
|
|
Packit |
9795e1 |
DEBUG("START dropping isoc packets %" PRIu64 " buffer > %" PRIu64 " hi threshold",
|
|
Packit |
9795e1 |
size, host->iso_threshold.higher);
|
|
Packit |
9795e1 |
host->iso_threshold.dropping = true;
|
|
Packit |
9795e1 |
} else if (size < host->iso_threshold.lower) {
|
|
Packit |
9795e1 |
if (host->iso_threshold.dropping)
|
|
Packit |
9795e1 |
DEBUG("STOP dropping isoc packets %" PRIu64 " buffer < %" PRIu64 " low threshold",
|
|
Packit |
9795e1 |
size, host->iso_threshold.lower);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->iso_threshold.dropping = false;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return !host->iso_threshold.dropping;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_send_stream_data(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep, uint8_t status, uint8_t *data, int len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
/* USB-2 is max 8000 packets / sec, if we've queued up more then 0.1 sec,
|
|
Packit |
9795e1 |
assume our connection is not keeping up and start dropping packets. */
|
|
Packit |
9795e1 |
if (usbredirparser_has_data_to_write(host->parser) > 800) {
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].warn_on_drop) {
|
|
Packit |
9795e1 |
WARNING("buffered stream on endpoint %02X, connection too slow, "
|
|
Packit |
9795e1 |
"dropping packets", ep);
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].warn_on_drop = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
DEBUG("buffered complete ep %02X dropping packet status %d len %d",
|
|
Packit |
9795e1 |
ep, status, len);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("buffered complete ep %02X status %d len %d", ep, status, len);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
switch (host->endpoint[EP2I(ep)].type) {
|
|
Packit |
9795e1 |
case usb_redir_type_iso: {
|
|
Packit |
9795e1 |
struct usb_redir_iso_packet_header iso_packet = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
.length = len,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (usbredirhost_can_write_iso_package(host))
|
|
Packit |
9795e1 |
usbredirparser_send_iso_packet(host->parser, id, &iso_packet,
|
|
Packit |
9795e1 |
data, len);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
case usb_redir_type_bulk: {
|
|
Packit |
9795e1 |
struct usb_redir_buffered_bulk_packet_header bulk_packet = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
.length = len,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_buffered_bulk_packet(host->parser, id,
|
|
Packit |
9795e1 |
&bulk_packet, data, len);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
case usb_redir_type_interrupt: {
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header interrupt_packet = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
.length = len,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_interrupt_packet(host->parser, id,
|
|
Packit |
9795e1 |
&interrupt_packet, data, len);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from both parser read and packet complete callbacks */
|
|
Packit |
9795e1 |
static int usbredirhost_submit_stream_transfer_unlocked(
|
|
Packit |
9795e1 |
struct usbredirhost *host, struct usbredirtransfer *transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_submit_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
uint8_t ep = transfer->transfer->endpoint;
|
|
Packit |
9795e1 |
if (r == LIBUSB_ERROR_NO_DEVICE) {
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
ERROR("error submitting transfer on ep %02X: %s, stopping stream",
|
|
Packit |
9795e1 |
ep, libusb_error_name(r));
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, transfer->id, ep,
|
|
Packit |
9795e1 |
usb_redir_stall);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
return usb_redir_stall;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
transfer->packet_idx = SUBMITTED_IDX;
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from both parser read and packet complete callbacks */
|
|
Packit |
9795e1 |
static int usbredirhost_start_stream_unlocked(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint8_t ep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
unsigned int i, count = host->endpoint[EP2I(ep)].transfer_count;
|
|
Packit |
9795e1 |
int status;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* For out endpoints 1/2 the transfers are a buffer for usb-guest data */
|
|
Packit |
9795e1 |
if (!(ep & LIBUSB_ENDPOINT_IN)) {
|
|
Packit |
9795e1 |
count /= 2;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
for (i = 0; i < count; i++) {
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->id =
|
|
Packit |
9795e1 |
i * host->endpoint[EP2I(ep)].pkts_per_transfer;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
status = usbredirhost_submit_stream_transfer_unlocked(host,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]);
|
|
Packit |
9795e1 |
if (status != usb_redir_success) {
|
|
Packit |
9795e1 |
return status;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].stream_started = 1;
|
|
Packit |
9795e1 |
return usb_redir_success;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_stop_stream(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream(host, ep);
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, usb_redir_success);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_set_iso_threshold(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint8_t pkts_per_transfer, uint8_t transfer_count, uint16_t max_packetsize)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
uint64_t reference = pkts_per_transfer * transfer_count * max_packetsize;
|
|
Packit |
9795e1 |
host->iso_threshold.lower = reference / 2;
|
|
Packit |
9795e1 |
host->iso_threshold.higher = reference * 3;
|
|
Packit |
9795e1 |
DEBUG("higher threshold is %" PRIu64 " bytes | lower threshold is %" PRIu64 " bytes",
|
|
Packit |
9795e1 |
host->iso_threshold.higher, host->iso_threshold.lower);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from both parser read and packet complete callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_alloc_stream_unlocked(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep, uint8_t type, uint8_t pkts_per_transfer,
|
|
Packit |
9795e1 |
int pkt_size, uint8_t transfer_count, int send_success)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, buf_size, status = usb_redir_success;
|
|
Packit |
9795e1 |
unsigned char *buffer;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
goto error;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].type != type) {
|
|
Packit |
9795e1 |
ERROR("error start stream type %d on type %d endpoint",
|
|
Packit |
9795e1 |
type, host->endpoint[EP2I(ep)].type);
|
|
Packit |
9795e1 |
goto error;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if ( pkts_per_transfer < 1 ||
|
|
Packit |
9795e1 |
pkts_per_transfer > MAX_PACKETS_PER_TRANSFER ||
|
|
Packit |
9795e1 |
transfer_count < 1 ||
|
|
Packit |
9795e1 |
transfer_count > MAX_TRANSFER_COUNT ||
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].max_packetsize == 0 ||
|
|
Packit |
9795e1 |
(pkt_size % host->endpoint[EP2I(ep)].max_packetsize) != 0) {
|
|
Packit |
9795e1 |
ERROR("error start stream type %d invalid parameters", type);
|
|
Packit |
9795e1 |
goto error;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].transfer_count) {
|
|
Packit |
9795e1 |
ERROR("error received start type %d for already started stream", type);
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, usb_redir_inval);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("allocating stream ep %02X type %d packet-size %d pkts %d urbs %d",
|
|
Packit |
9795e1 |
ep, type, pkt_size, pkts_per_transfer, transfer_count);
|
|
Packit |
9795e1 |
for (i = 0; i < transfer_count; i++) {
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i] =
|
|
Packit |
9795e1 |
usbredirhost_alloc_transfer(host, (type == usb_redir_type_iso) ?
|
|
Packit |
9795e1 |
pkts_per_transfer : 0);
|
|
Packit |
9795e1 |
if (!host->endpoint[EP2I(ep)].transfer[i]) {
|
|
Packit |
9795e1 |
goto alloc_error;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
buf_size = pkt_size * pkts_per_transfer;
|
|
Packit |
9795e1 |
buffer = malloc(buf_size);
|
|
Packit |
9795e1 |
if (!buffer) {
|
|
Packit |
9795e1 |
goto alloc_error;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
switch (type) {
|
|
Packit |
9795e1 |
case usb_redir_type_iso:
|
|
Packit |
9795e1 |
libusb_fill_iso_transfer(
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->transfer, host->handle,
|
|
Packit |
9795e1 |
ep, buffer, buf_size, pkts_per_transfer,
|
|
Packit |
9795e1 |
usbredirhost_iso_packet_complete,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i], ISO_TIMEOUT);
|
|
Packit |
9795e1 |
libusb_set_iso_packet_lengths(
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->transfer, pkt_size);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_set_iso_threshold(
|
|
Packit |
9795e1 |
host, pkts_per_transfer, transfer_count,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].max_packetsize);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case usb_redir_type_bulk:
|
|
Packit |
9795e1 |
libusb_fill_bulk_transfer(
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->transfer, host->handle,
|
|
Packit |
9795e1 |
ep, buffer, buf_size, usbredirhost_buffered_packet_complete,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i], BULK_TIMEOUT);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case usb_redir_type_interrupt:
|
|
Packit |
9795e1 |
libusb_fill_interrupt_transfer(
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->transfer, host->handle,
|
|
Packit |
9795e1 |
ep, buffer, buf_size, usbredirhost_buffered_packet_complete,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i], INTERRUPT_TIMEOUT);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].out_idx = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].drop_packets = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].pkts_per_transfer = pkts_per_transfer;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer_count = transfer_count;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* For input endpoints submit the transfers now */
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
status = usbredirhost_start_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (send_success && status == usb_redir_success) {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, status);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
alloc_error:
|
|
Packit |
9795e1 |
ERROR("out of memory allocating type %d stream buffers", type);
|
|
Packit |
9795e1 |
do {
|
|
Packit |
9795e1 |
usbredirhost_free_transfer(host->endpoint[EP2I(ep)].transfer[i]);
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i] = NULL;
|
|
Packit |
9795e1 |
i--;
|
|
Packit |
9795e1 |
} while (i >= 0);
|
|
Packit |
9795e1 |
error:
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, usb_redir_stall);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_alloc_stream(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep, uint8_t type, uint8_t pkts_per_transfer,
|
|
Packit |
9795e1 |
int pkt_size, uint8_t transfer_count, int send_success)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
usbredirhost_alloc_stream_unlocked(host, id, ep, type, pkts_per_transfer,
|
|
Packit |
9795e1 |
pkt_size, transfer_count, send_success);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_clear_stream_stall_unlocked(
|
|
Packit |
9795e1 |
struct usbredirhost *host, uint64_t id, uint8_t ep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
uint8_t pkts_per_transfer = host->endpoint[EP2I(ep)].pkts_per_transfer;
|
|
Packit |
9795e1 |
uint8_t transfer_count = host->endpoint[EP2I(ep)].transfer_count;
|
|
Packit |
9795e1 |
int pkt_size = host->endpoint[EP2I(ep)].transfer[0]->transfer->length /
|
|
Packit |
9795e1 |
pkts_per_transfer;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
WARNING("buffered stream on endpoint %02X stalled, clearing stall", ep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
r = libusb_clear_halt(host->handle, ep);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, usb_redir_stall);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
usbredirhost_alloc_stream_unlocked(host, id, ep,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].type,
|
|
Packit |
9795e1 |
pkts_per_transfer, pkt_size,
|
|
Packit |
9795e1 |
transfer_count, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from close and parser read callbacks */
|
|
Packit |
9795e1 |
static int usbredirhost_cancel_pending_urbs(struct usbredirhost *host,
|
|
Packit |
9795e1 |
int notify_guest)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *t;
|
|
Packit |
9795e1 |
int i, wait;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
|
Packit |
9795e1 |
if (notify_guest && host->endpoint[i].transfer_count)
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, 0, I2EP(i), usb_redir_stall);
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream_unlocked(host, I2EP(i));
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
wait = host->cancels_pending;
|
|
Packit |
9795e1 |
for (t = host->transfers_head.next; t; t = t->next) {
|
|
Packit |
9795e1 |
libusb_cancel_transfer(t->transfer);
|
|
Packit |
9795e1 |
wait = 1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (notify_guest)
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return wait;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Called from close and parser read callbacks */
|
|
Packit |
9795e1 |
void usbredirhost_wait_for_cancel_completion(struct usbredirhost *host)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int wait;
|
|
Packit |
9795e1 |
struct timeval tv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
do {
|
|
Packit |
9795e1 |
memset(&tv, 0, sizeof(tv));
|
|
Packit |
9795e1 |
tv.tv_usec = 2500;
|
|
Packit |
9795e1 |
libusb_handle_events_timeout(host->ctx, &tv;;
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
wait = host->cancels_pending || host->transfers_head.next;
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
} while (wait);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Only called from read callbacks */
|
|
Packit |
9795e1 |
static void usbredirhost_cancel_pending_urbs_on_interface(
|
|
Packit |
9795e1 |
struct usbredirhost *host, int i)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *t;
|
|
Packit |
9795e1 |
const struct libusb_interface_descriptor *intf_desc;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
intf_desc = &host->config->interface[i].altsetting[host->alt_setting[i]];
|
|
Packit |
9795e1 |
for (i = 0; i < intf_desc->bNumEndpoints; i++) {
|
|
Packit |
9795e1 |
uint8_t ep = intf_desc->endpoint[i].bEndpointAddress;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_cancel_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (t = host->transfers_head.next; t; t = t->next) {
|
|
Packit |
9795e1 |
if (t->transfer->endpoint == ep)
|
|
Packit |
9795e1 |
libusb_cancel_transfer(t->transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Only called from read callbacks */
|
|
Packit |
9795e1 |
static int usbredirhost_bInterfaceNumber_to_index(
|
|
Packit |
9795e1 |
struct usbredirhost *host, uint8_t bInterfaceNumber)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, n;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; host->config && i < host->config->bNumInterfaces; i++) {
|
|
Packit |
9795e1 |
n = host->config->interface[i].altsetting[0].bInterfaceNumber;
|
|
Packit |
9795e1 |
if (n == bInterfaceNumber) {
|
|
Packit |
9795e1 |
return i;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
ERROR("invalid bNumInterface: %d\n", (int)bInterfaceNumber);
|
|
Packit |
9795e1 |
return -1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_log_data(struct usbredirhost *host, const char *desc,
|
|
Packit |
9795e1 |
const uint8_t *data, int len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (usbredirparser_debug_data <= host->verbose) {
|
|
Packit |
9795e1 |
int i, j, n;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < len; i += j) {
|
|
Packit |
9795e1 |
char buf[128];
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
n = sprintf(buf, "%s", desc);
|
|
Packit |
9795e1 |
for (j = 0; j < 8 && i + j < len; j++){
|
|
Packit |
9795e1 |
n += sprintf(buf + n, " %02X", data[i + j]);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
va_log(host, usbredirparser_debug_data, "%s", buf);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void usbredirhost_set_buffered_output_size_cb(struct usbredirhost *host,
|
|
Packit |
9795e1 |
usbredirhost_buffered_output_size buffered_output_size_func)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
if (!host) {
|
|
Packit |
9795e1 |
fprintf(stderr, "%s: invalid usbredirhost", __func__);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->buffered_output_size_func = buffered_output_size_func;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Return value:
|
|
Packit |
9795e1 |
0 All ok
|
|
Packit |
9795e1 |
1 Packet borked, continue with next packet / urb
|
|
Packit |
9795e1 |
2 Stream borked, full stop, no resubmit, etc.
|
|
Packit |
9795e1 |
Note in the case of a return value of 2 this function takes care of
|
|
Packit |
9795e1 |
sending an iso status message to the usb-guest. */
|
|
Packit |
9795e1 |
static int usbredirhost_handle_iso_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, uint8_t ep, int r)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
switch (r) {
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_COMPLETED:
|
|
Packit |
9795e1 |
case -EXDEV: /* FIXlibusb: Passing regular error codes, bad libusb, bad! */
|
|
Packit |
9795e1 |
return 0;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_CANCELLED:
|
|
Packit |
9795e1 |
/* Stream was intentionally stopped */
|
|
Packit |
9795e1 |
return 2;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_STALL:
|
|
Packit |
9795e1 |
usbredirhost_clear_stream_stall_unlocked(host, id, ep);
|
|
Packit |
9795e1 |
return 2;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_NO_DEVICE:
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
return 2;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_OVERFLOW:
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_ERROR:
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TIMED_OUT:
|
|
Packit |
9795e1 |
default:
|
|
Packit |
9795e1 |
ERROR("iso stream error on endpoint %02X: %d", ep, r);
|
|
Packit |
9795e1 |
return 1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_iso_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = libusb_transfer->user_data;
|
|
Packit |
9795e1 |
uint8_t ep = libusb_transfer->endpoint;
|
|
Packit |
9795e1 |
struct usbredirhost *host = transfer->host;
|
|
Packit |
9795e1 |
int i, r, len, status;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
if (transfer->cancelled) {
|
|
Packit |
9795e1 |
host->cancels_pending--;
|
|
Packit |
9795e1 |
usbredirhost_free_transfer(transfer);
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Mark transfer completed (iow not submitted) */
|
|
Packit |
9795e1 |
transfer->packet_idx = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Check overal transfer status */
|
|
Packit |
9795e1 |
r = libusb_transfer->status;
|
|
Packit |
9795e1 |
switch (usbredirhost_handle_iso_status(host, transfer->id, ep, r)) {
|
|
Packit |
9795e1 |
case 0:
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case 1:
|
|
Packit |
9795e1 |
status = libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
struct usb_redir_iso_packet_header iso_packet = {
|
|
Packit |
9795e1 |
.endpoint = ep,
|
|
Packit |
9795e1 |
.status = status,
|
|
Packit |
9795e1 |
.length = 0
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
usbredirparser_send_iso_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&iso_packet, NULL, 0);
|
|
Packit |
9795e1 |
transfer->id += libusb_transfer->num_iso_packets;
|
|
Packit |
9795e1 |
goto resubmit;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, transfer->id, ep, status);
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case 2:
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Check per packet status and send ok input packets to usb-guest */
|
|
Packit |
9795e1 |
for (i = 0; i < libusb_transfer->num_iso_packets; i++) {
|
|
Packit |
9795e1 |
r = libusb_transfer->iso_packet_desc[i].status;
|
|
Packit |
9795e1 |
len = libusb_transfer->iso_packet_desc[i].actual_length;
|
|
Packit |
9795e1 |
status = libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
switch (usbredirhost_handle_iso_status(host, transfer->id, ep, r)) {
|
|
Packit |
9795e1 |
case 0:
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case 1:
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
len = 0;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, transfer->id, ep,
|
|
Packit |
9795e1 |
status);
|
|
Packit |
9795e1 |
goto unlock; /* We send max one iso status message per urb */
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case 2:
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_data(host, transfer->id, ep, status,
|
|
Packit |
9795e1 |
libusb_get_iso_packet_buffer(libusb_transfer, i), len);
|
|
Packit |
9795e1 |
transfer->id++;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
DEBUG("iso-in complete ep %02X pkt %d len %d id %"PRIu64,
|
|
Packit |
9795e1 |
ep, i, len, transfer->id);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* And for input transfers resubmit the transfer (output transfers
|
|
Packit |
9795e1 |
get resubmitted when they have all their packets filled with data) */
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
resubmit:
|
|
Packit |
9795e1 |
transfer->id += (host->endpoint[EP2I(ep)].transfer_count - 1) *
|
|
Packit |
9795e1 |
libusb_transfer->num_iso_packets;
|
|
Packit |
9795e1 |
usbredirhost_submit_stream_transfer_unlocked(host, transfer);
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
for (i = 0; i < host->endpoint[EP2I(ep)].transfer_count; i++) {
|
|
Packit |
9795e1 |
transfer = host->endpoint[EP2I(ep)].transfer[i];
|
|
Packit |
9795e1 |
if (transfer->packet_idx == SUBMITTED_IDX)
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (i == host->endpoint[EP2I(ep)].transfer_count) {
|
|
Packit |
9795e1 |
DEBUG("underflow of iso out queue on ep: %02X", ep);
|
|
Packit |
9795e1 |
/* Re-fill buffers before submitting urbs again */
|
|
Packit |
9795e1 |
for (i = 0; i < host->endpoint[EP2I(ep)].transfer_count; i++)
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer[i]->packet_idx = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].out_idx = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].stream_started = 0;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].drop_packets = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
unlock:
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_buffered_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = libusb_transfer->user_data;
|
|
Packit |
9795e1 |
uint8_t ep = libusb_transfer->endpoint;
|
|
Packit |
9795e1 |
struct usbredirhost *host = transfer->host;
|
|
Packit |
9795e1 |
int r, len = libusb_transfer->actual_length;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (transfer->cancelled) {
|
|
Packit |
9795e1 |
host->cancels_pending--;
|
|
Packit |
9795e1 |
usbredirhost_free_transfer(transfer);
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Mark transfer completed (iow not submitted) */
|
|
Packit |
9795e1 |
transfer->packet_idx = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_transfer->status;
|
|
Packit |
9795e1 |
switch (r) {
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_COMPLETED:
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_STALL:
|
|
Packit |
9795e1 |
usbredirhost_clear_stream_stall_unlocked(host, transfer->id, ep);
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_NO_DEVICE:
|
|
Packit |
9795e1 |
usbredirhost_handle_disconnect(host);
|
|
Packit |
9795e1 |
goto unlock;
|
|
Packit |
9795e1 |
default:
|
|
Packit |
9795e1 |
ERROR("buffered in error on endpoint %02X: %d", ep, r);
|
|
Packit |
9795e1 |
len = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_send_stream_data(host, transfer->id, ep,
|
|
Packit |
9795e1 |
libusb_status_or_error_to_redir_status(host, r),
|
|
Packit |
9795e1 |
transfer->transfer->buffer, len);
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "buffered data in:",
|
|
Packit |
9795e1 |
transfer->transfer->buffer, len);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
transfer->id += host->endpoint[EP2I(ep)].transfer_count;
|
|
Packit |
9795e1 |
usbredirhost_submit_stream_transfer_unlocked(host, transfer);
|
|
Packit |
9795e1 |
unlock:
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_hello(void *priv, struct usb_redir_hello_header *h)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->connect_pending)
|
|
Packit |
9795e1 |
usbredirhost_send_device_connect(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_reset(void *priv)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected || host->reset) {
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/*
|
|
Packit |
9795e1 |
* The guest should have cancelled any pending urbs already, but the
|
|
Packit |
9795e1 |
* cancellations may be awaiting completion, and if we then do a reset
|
|
Packit |
9795e1 |
* they will complete with an error code of LIBUSB_TRANSFER_NO_DEVICE.
|
|
Packit |
9795e1 |
*
|
|
Packit |
9795e1 |
* And we also need to cleanly shutdown any streams (and let the guest
|
|
Packit |
9795e1 |
* know they should be restarted after the reset).
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
if (usbredirhost_cancel_pending_urbs(host, 1))
|
|
Packit |
9795e1 |
usbredirhost_wait_for_cancel_completion(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = usbredirhost_reset_device(host);
|
|
Packit |
9795e1 |
if (r != 0) {
|
|
Packit |
9795e1 |
host->read_status = usbredirhost_read_device_lost;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_set_configuration(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_set_configuration_header *set_config)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
int r, claim_status;
|
|
Packit |
9795e1 |
struct usb_redir_configuration_status_header status = {
|
|
Packit |
9795e1 |
.status = usb_redir_success,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
goto exit;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->config &&
|
|
Packit |
9795e1 |
host->config->bConfigurationValue == set_config->configuration) {
|
|
Packit |
9795e1 |
goto exit;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_cancel_pending_urbs(host, 0);
|
|
Packit |
9795e1 |
usbredirhost_release(host, 0);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_set_configuration(host->handle, set_config->configuration);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("could not set active configuration to %d: %s",
|
|
Packit |
9795e1 |
(int)set_config->configuration, libusb_error_name(r));
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
claim_status = usbredirhost_claim(host, 0);
|
|
Packit |
9795e1 |
if (claim_status != usb_redir_success) {
|
|
Packit |
9795e1 |
usbredirhost_clear_device(host);
|
|
Packit |
9795e1 |
host->read_status = usbredirhost_read_device_lost;
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
goto exit;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_send_interface_n_ep_info(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
exit:
|
|
Packit |
9795e1 |
status.configuration = host->config ? host->config->bConfigurationValue:0;
|
|
Packit |
9795e1 |
usbredirparser_send_configuration_status(host->parser, id, &status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_get_configuration(void *priv, uint64_t id)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
struct usb_redir_configuration_status_header status;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected)
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
status.status = usb_redir_success;
|
|
Packit |
9795e1 |
status.configuration = host->config ? host->config->bConfigurationValue:0;
|
|
Packit |
9795e1 |
usbredirparser_send_configuration_status(host->parser, id, &status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_set_alt_setting(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_set_alt_setting_header *set_alt_setting)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
int i, j, r;
|
|
Packit |
9795e1 |
struct usb_redir_alt_setting_status_header status = {
|
|
Packit |
9795e1 |
.status = usb_redir_success,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
status.alt = -1;
|
|
Packit |
9795e1 |
goto exit_unknown_interface;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
i = usbredirhost_bInterfaceNumber_to_index(host,
|
|
Packit |
9795e1 |
set_alt_setting->interface);
|
|
Packit |
9795e1 |
if (i == -1) {
|
|
Packit |
9795e1 |
status.status = usb_redir_inval;
|
|
Packit |
9795e1 |
status.alt = -1;
|
|
Packit |
9795e1 |
goto exit_unknown_interface;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_cancel_pending_urbs_on_interface(host, i);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_set_interface_alt_setting(host->handle,
|
|
Packit |
9795e1 |
set_alt_setting->interface,
|
|
Packit |
9795e1 |
set_alt_setting->alt);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("could not set alt setting for interface %d to %d: %s",
|
|
Packit |
9795e1 |
set_alt_setting->interface, set_alt_setting->alt,
|
|
Packit |
9795e1 |
libusb_error_name(r));
|
|
Packit |
9795e1 |
status.status = libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
goto exit;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* The new alt setting may have lost endpoints compared to the old! ->
|
|
Packit |
9795e1 |
Clear settings for all endpoints which used to be part of the intf. */
|
|
Packit |
9795e1 |
for (j = 0; j < MAX_ENDPOINTS; j++) {
|
|
Packit |
9795e1 |
if (host->endpoint[j].interface != set_alt_setting->interface)
|
|
Packit |
9795e1 |
continue;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if ((j & 0x0f) == 0) {
|
|
Packit |
9795e1 |
host->endpoint[j].type = usb_redir_type_control;
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
host->endpoint[j].type = usb_redir_type_invalid;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
host->endpoint[j].interval = 0;
|
|
Packit |
9795e1 |
host->endpoint[j].interface = 0;
|
|
Packit |
9795e1 |
host->endpoint[j].max_packetsize = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->alt_setting[i] = set_alt_setting->alt;
|
|
Packit |
9795e1 |
usbredirhost_parse_interface(host, i);
|
|
Packit |
9795e1 |
usbredirhost_send_interface_n_ep_info(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
exit:
|
|
Packit |
9795e1 |
status.alt = host->alt_setting[i];
|
|
Packit |
9795e1 |
exit_unknown_interface:
|
|
Packit |
9795e1 |
status.interface = set_alt_setting->interface;
|
|
Packit |
9795e1 |
usbredirparser_send_alt_setting_status(host->parser, id, &status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_get_alt_setting(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_get_alt_setting_header *get_alt_setting)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
struct usb_redir_alt_setting_status_header status;
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
status.alt = -1;
|
|
Packit |
9795e1 |
goto exit;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
i = usbredirhost_bInterfaceNumber_to_index(host,
|
|
Packit |
9795e1 |
get_alt_setting->interface);
|
|
Packit |
9795e1 |
if (i >= 0) {
|
|
Packit |
9795e1 |
status.status = usb_redir_success;
|
|
Packit |
9795e1 |
status.alt = host->alt_setting[i];
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
status.status = usb_redir_inval;
|
|
Packit |
9795e1 |
status.alt = -1;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
exit:
|
|
Packit |
9795e1 |
status.interface = get_alt_setting->interface;
|
|
Packit |
9795e1 |
usbredirparser_send_alt_setting_status(host->parser, id, &status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_start_iso_stream(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_iso_stream_header *start_iso_stream)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = start_iso_stream->endpoint;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_alloc_stream(host, id, ep, usb_redir_type_iso,
|
|
Packit |
9795e1 |
start_iso_stream->pkts_per_urb,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].max_packetsize,
|
|
Packit |
9795e1 |
start_iso_stream->no_urbs, 1);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_stop_iso_stream(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_iso_stream_header *stop_iso_stream)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
usbredirhost_stop_stream(priv, id, stop_iso_stream->endpoint);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_start_interrupt_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = start_interrupt_receiving->endpoint;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_alloc_stream(host, id, ep, usb_redir_type_interrupt, 1,
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].max_packetsize,
|
|
Packit |
9795e1 |
INTERRUPT_TRANSFER_COUNT, 1);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_stop_interrupt_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
usbredirhost_stop_stream(priv, id, stop_interrupt_receiving->endpoint);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
static int usbredirhost_ep_mask_to_eps(uint32_t ep_mask, unsigned char *eps)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, j;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0, j = 0; i < MAX_ENDPOINTS; i++) {
|
|
Packit |
9795e1 |
if (ep_mask & (1 << i))
|
|
Packit |
9795e1 |
eps[j++] = I2EP(i);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return j;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_alloc_bulk_streams(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
unsigned char eps[MAX_ENDPOINTS];
|
|
Packit |
9795e1 |
int r, no_eps;
|
|
Packit |
9795e1 |
struct usb_redir_bulk_streams_status_header streams_status = {
|
|
Packit |
9795e1 |
.endpoints = alloc_bulk_streams->endpoints,
|
|
Packit |
9795e1 |
.no_streams = alloc_bulk_streams->no_streams,
|
|
Packit |
9795e1 |
.status = usb_redir_success,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
no_eps = usbredirhost_ep_mask_to_eps(alloc_bulk_streams->endpoints, eps);
|
|
Packit |
9795e1 |
r = libusb_alloc_streams(host->handle, alloc_bulk_streams->no_streams,
|
|
Packit |
9795e1 |
eps, no_eps);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("could not alloc bulk streams: %s", libusb_error_name(r));
|
|
Packit |
9795e1 |
streams_status.status =
|
|
Packit |
9795e1 |
libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
} else if (r < alloc_bulk_streams->no_streams) {
|
|
Packit |
9795e1 |
ERROR("tried to alloc %u bulk streams but got only %d",
|
|
Packit |
9795e1 |
alloc_bulk_streams->no_streams, r);
|
|
Packit |
9795e1 |
streams_status.status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_streams_status(host->parser, id, &streams_status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_free_bulk_streams(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_free_bulk_streams_header *free_bulk_streams)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
unsigned char eps[MAX_ENDPOINTS];
|
|
Packit |
9795e1 |
int r, no_eps;
|
|
Packit |
9795e1 |
struct usb_redir_bulk_streams_status_header streams_status = {
|
|
Packit |
9795e1 |
.endpoints = free_bulk_streams->endpoints,
|
|
Packit |
9795e1 |
.no_streams = 0,
|
|
Packit |
9795e1 |
.status = usb_redir_success,
|
|
Packit |
9795e1 |
};
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
no_eps = usbredirhost_ep_mask_to_eps(free_bulk_streams->endpoints, eps);
|
|
Packit |
9795e1 |
r = libusb_free_streams(host->handle, eps, no_eps);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("could not free bulk streams: %s", libusb_error_name(r));
|
|
Packit |
9795e1 |
streams_status.status =
|
|
Packit |
9795e1 |
libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_streams_status(host->parser, id, &streams_status);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_filter_reject(void *priv)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected)
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
INFO("device rejected");
|
|
Packit |
9795e1 |
host->read_status = usbredirhost_read_device_rejected;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_filter_filter(void *priv,
|
|
Packit |
9795e1 |
struct usbredirfilter_rule *rules, int rules_count)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
free(host->filter_rules);
|
|
Packit |
9795e1 |
host->filter_rules = rules;
|
|
Packit |
9795e1 |
host->filter_rules_count = rules_count;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_device_disconnect_ack(void *priv)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!host->wait_disconnect) {
|
|
Packit |
9795e1 |
ERROR("error received disconnect ack without sending a disconnect");
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->wait_disconnect = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->connect_pending)
|
|
Packit |
9795e1 |
usbredirhost_send_device_connect(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_start_bulk_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_start_bulk_receiving_header *start_bulk_receiving)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = start_bulk_receiving->endpoint;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_alloc_stream(host, id, ep, usb_redir_type_bulk, 1,
|
|
Packit |
9795e1 |
start_bulk_receiving->bytes_per_transfer,
|
|
Packit |
9795e1 |
start_bulk_receiving->no_transfers, 1);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_stop_bulk_receiving(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
usbredirhost_stop_stream(priv, id, stop_bulk_receiving->endpoint);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_cancel_data_packet(void *priv, uint64_t id)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
struct usbredirtransfer *t;
|
|
Packit |
9795e1 |
struct usb_redir_control_packet_header control_packet;
|
|
Packit |
9795e1 |
struct usb_redir_bulk_packet_header bulk_packet;
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header interrupt_packet;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/*
|
|
Packit |
9795e1 |
* This is a bit tricky, we are run from a parser read callback, while
|
|
Packit |
9795e1 |
* at the same time the packet completion callback may run from another
|
|
Packit |
9795e1 |
* thread.
|
|
Packit |
9795e1 |
*
|
|
Packit |
9795e1 |
* Since the completion handler will remove the transfer from our list,
|
|
Packit |
9795e1 |
* send it back to the usb-guest (which we don't want to do twice),
|
|
Packit |
9795e1 |
* and *free* the transfer, we must do the libusb_cancel_transfer()
|
|
Packit |
9795e1 |
* with the lock held to ensure that it is not freed while we try to
|
|
Packit |
9795e1 |
* cancel it.
|
|
Packit |
9795e1 |
*
|
|
Packit |
9795e1 |
* Doing this means libusb taking the transfer lock, while
|
|
Packit |
9795e1 |
* we are holding our own lock, this is ok, since libusb releases the
|
|
Packit |
9795e1 |
* transfer lock before calling the packet completion callback, so there
|
|
Packit |
9795e1 |
* is no deadlock here.
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
for (t = host->transfers_head.next; t; t = t->next) {
|
|
Packit |
9795e1 |
/* After cancellation the guest may re-use the id, so skip already
|
|
Packit |
9795e1 |
cancelled packets */
|
|
Packit |
9795e1 |
if (!t->cancelled && t->id == id) {
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/*
|
|
Packit |
9795e1 |
* Note not finding the transfer is not an error, the transfer may have
|
|
Packit |
9795e1 |
* completed by the time we receive the cancel.
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
if (t) {
|
|
Packit |
9795e1 |
t->cancelled = 1;
|
|
Packit |
9795e1 |
libusb_cancel_transfer(t->transfer);
|
|
Packit |
9795e1 |
switch(t->transfer->type) {
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TYPE_CONTROL:
|
|
Packit |
9795e1 |
control_packet = t->control_packet;
|
|
Packit |
9795e1 |
control_packet.status = usb_redir_cancelled;
|
|
Packit |
9795e1 |
control_packet.length = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_control_packet(host->parser, t->id,
|
|
Packit |
9795e1 |
&control_packet, NULL, 0);
|
|
Packit |
9795e1 |
DEBUG("cancelled control packet ep %02x id %"PRIu64,
|
|
Packit |
9795e1 |
control_packet.endpoint, id);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TYPE_BULK:
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
bulk_packet = t->bulk_packet;
|
|
Packit |
9795e1 |
bulk_packet.status = usb_redir_cancelled;
|
|
Packit |
9795e1 |
bulk_packet.length = 0;
|
|
Packit |
9795e1 |
bulk_packet.length_high = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_packet(host->parser, t->id,
|
|
Packit |
9795e1 |
&bulk_packet, NULL, 0);
|
|
Packit |
9795e1 |
DEBUG("cancelled bulk packet ep %02x id %"PRIu64,
|
|
Packit |
9795e1 |
bulk_packet.endpoint, id);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
|
|
Packit |
9795e1 |
interrupt_packet = t->interrupt_packet;
|
|
Packit |
9795e1 |
interrupt_packet.status = usb_redir_cancelled;
|
|
Packit |
9795e1 |
interrupt_packet.length = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_interrupt_packet(host->parser, t->id,
|
|
Packit |
9795e1 |
&interrupt_packet, NULL, 0);
|
|
Packit |
9795e1 |
DEBUG("cancelled interrupt packet ep %02x id %"PRIu64,
|
|
Packit |
9795e1 |
interrupt_packet.endpoint, id);
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
} else
|
|
Packit |
9795e1 |
DEBUG("cancel packet id %"PRIu64" not found", id);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_control_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usb_redir_control_packet_header control_packet;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = libusb_transfer->user_data;
|
|
Packit |
9795e1 |
struct usbredirhost *host = transfer->host;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
control_packet = transfer->control_packet;
|
|
Packit |
9795e1 |
control_packet.status = libusb_status_or_error_to_redir_status(host,
|
|
Packit |
9795e1 |
libusb_transfer->status);
|
|
Packit |
9795e1 |
control_packet.length = libusb_transfer->actual_length;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("control complete ep %02X status %d len %d id %"PRIu64,
|
|
Packit |
9795e1 |
control_packet.endpoint, control_packet.status,
|
|
Packit |
9795e1 |
control_packet.length, transfer->id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!transfer->cancelled) {
|
|
Packit |
9795e1 |
if (control_packet.endpoint & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "ctrl data in:",
|
|
Packit |
9795e1 |
libusb_transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE,
|
|
Packit |
9795e1 |
libusb_transfer->actual_length);
|
|
Packit |
9795e1 |
usbredirparser_send_control_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&control_packet,
|
|
Packit |
9795e1 |
libusb_transfer->buffer +
|
|
Packit |
9795e1 |
LIBUSB_CONTROL_SETUP_SIZE,
|
|
Packit |
9795e1 |
libusb_transfer->actual_length);
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirparser_send_control_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&control_packet, NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_remove_and_free_transfer(transfer);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_send_control_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, struct usb_redir_control_packet_header *control_packet,
|
|
Packit |
9795e1 |
uint8_t status)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
control_packet->status = status;
|
|
Packit |
9795e1 |
control_packet->length = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_control_packet(host->parser, id, control_packet,
|
|
Packit |
9795e1 |
NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_control_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_control_packet_header *control_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = control_packet->endpoint;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer;
|
|
Packit |
9795e1 |
unsigned char *buffer;
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("control submit ep %02X len %d id %"PRIu64, ep,
|
|
Packit |
9795e1 |
control_packet->length, id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
usbredirhost_send_control_status(host, id, control_packet,
|
|
Packit |
9795e1 |
usb_redir_ioerror);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Verify endpoint type */
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].type != usb_redir_type_control) {
|
|
Packit |
9795e1 |
ERROR("error control packet on non control ep %02X", ep);
|
|
Packit |
9795e1 |
usbredirhost_send_control_status(host, id, control_packet,
|
|
Packit |
9795e1 |
usb_redir_inval);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* If it is a clear stall, we need to do an actual clear stall, rather then
|
|
Packit |
9795e1 |
just forward the control packet, so that the usbhost usbstack knows
|
|
Packit |
9795e1 |
the stall is cleared */
|
|
Packit |
9795e1 |
if (control_packet->requesttype == LIBUSB_RECIPIENT_ENDPOINT &&
|
|
Packit |
9795e1 |
control_packet->request == LIBUSB_REQUEST_CLEAR_FEATURE &&
|
|
Packit |
9795e1 |
control_packet->value == 0x00 && data_len == 0) {
|
|
Packit |
9795e1 |
r = libusb_clear_halt(host->handle, control_packet->index);
|
|
Packit |
9795e1 |
r = libusb_status_or_error_to_redir_status(host, r);
|
|
Packit |
9795e1 |
DEBUG("clear halt ep %02X status %d", control_packet->index, r);
|
|
Packit |
9795e1 |
usbredirhost_send_control_status(host, id, control_packet, r);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + control_packet->length);
|
|
Packit |
9795e1 |
if (!buffer) {
|
|
Packit |
9795e1 |
ERROR("out of memory allocating transfer buffer, dropping packet");
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
transfer = usbredirhost_alloc_transfer(host, 0);
|
|
Packit |
9795e1 |
if (!transfer) {
|
|
Packit |
9795e1 |
free(buffer);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
libusb_fill_control_setup(buffer,
|
|
Packit |
9795e1 |
control_packet->requesttype,
|
|
Packit |
9795e1 |
control_packet->request,
|
|
Packit |
9795e1 |
control_packet->value,
|
|
Packit |
9795e1 |
control_packet->index,
|
|
Packit |
9795e1 |
control_packet->length);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!(ep & LIBUSB_ENDPOINT_IN)) {
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "ctrl data out:", data, data_len);
|
|
Packit |
9795e1 |
memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, data_len);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
libusb_fill_control_transfer(transfer->transfer, host->handle, buffer,
|
|
Packit |
9795e1 |
usbredirhost_control_packet_complete,
|
|
Packit |
9795e1 |
transfer, CTRL_TIMEOUT);
|
|
Packit |
9795e1 |
transfer->id = id;
|
|
Packit |
9795e1 |
transfer->control_packet = *control_packet;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_add_transfer(host, transfer);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_submit_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("error submitting control transfer on ep %02X: %s",
|
|
Packit |
9795e1 |
ep, libusb_error_name(r));
|
|
Packit |
9795e1 |
transfer->transfer->actual_length = 0;
|
|
Packit |
9795e1 |
transfer->transfer->status = r;
|
|
Packit |
9795e1 |
usbredirhost_control_packet_complete(transfer->transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_bulk_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usb_redir_bulk_packet_header bulk_packet;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = libusb_transfer->user_data;
|
|
Packit |
9795e1 |
struct usbredirhost *host = transfer->host;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
bulk_packet = transfer->bulk_packet;
|
|
Packit |
9795e1 |
bulk_packet.status = libusb_status_or_error_to_redir_status(host,
|
|
Packit |
9795e1 |
libusb_transfer->status);
|
|
Packit |
9795e1 |
bulk_packet.length = libusb_transfer->actual_length;
|
|
Packit |
9795e1 |
bulk_packet.length_high = libusb_transfer->actual_length >> 16;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("bulk complete ep %02X status %d len %d id %"PRIu64,
|
|
Packit |
9795e1 |
bulk_packet.endpoint, bulk_packet.status,
|
|
Packit |
9795e1 |
libusb_transfer->actual_length, transfer->id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!transfer->cancelled) {
|
|
Packit |
9795e1 |
if (bulk_packet.endpoint & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "bulk data in:",
|
|
Packit |
9795e1 |
libusb_transfer->buffer,
|
|
Packit |
9795e1 |
libusb_transfer->actual_length);
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&bulk_packet,
|
|
Packit |
9795e1 |
libusb_transfer->buffer,
|
|
Packit |
9795e1 |
libusb_transfer->actual_length);
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&bulk_packet, NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_remove_and_free_transfer(transfer);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_send_bulk_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, struct usb_redir_bulk_packet_header *bulk_packet,
|
|
Packit |
9795e1 |
uint8_t status)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
bulk_packet->status = status;
|
|
Packit |
9795e1 |
bulk_packet->length = 0;
|
|
Packit |
9795e1 |
bulk_packet->length_high = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_bulk_packet(host->parser, id, bulk_packet, NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_bulk_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_bulk_packet_header *bulk_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = bulk_packet->endpoint;
|
|
Packit |
9795e1 |
int len = (bulk_packet->length_high << 16) | bulk_packet->length;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer;
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("bulk submit ep %02X len %d id %"PRIu64, ep, len, id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
usbredirhost_send_bulk_status(host, id, bulk_packet,
|
|
Packit |
9795e1 |
usb_redir_ioerror);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].type != usb_redir_type_bulk) {
|
|
Packit |
9795e1 |
ERROR("error bulk packet on non bulk ep %02X", ep);
|
|
Packit |
9795e1 |
usbredirhost_send_bulk_status(host, id, bulk_packet, usb_redir_inval);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (ep & LIBUSB_ENDPOINT_IN) {
|
|
Packit |
9795e1 |
data = malloc(len);
|
|
Packit |
9795e1 |
if (!data) {
|
|
Packit |
9795e1 |
ERROR("out of memory allocating bulk buffer, dropping packet");
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "bulk data out:", data, data_len);
|
|
Packit |
9795e1 |
/* Note no memcpy, we can re-use the data buffer the parser
|
|
Packit |
9795e1 |
malloc-ed for us and expects us to free */
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
transfer = usbredirhost_alloc_transfer(host, 0);
|
|
Packit |
9795e1 |
if (!transfer) {
|
|
Packit |
9795e1 |
free(data);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (bulk_packet->stream_id) {
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION >= 0x01000103
|
|
Packit |
9795e1 |
libusb_fill_bulk_stream_transfer(transfer->transfer, host->handle, ep,
|
|
Packit |
9795e1 |
bulk_packet->stream_id, data, len,
|
|
Packit |
9795e1 |
usbredirhost_bulk_packet_complete,
|
|
Packit |
9795e1 |
transfer, BULK_TIMEOUT);
|
|
Packit |
9795e1 |
#else
|
|
Packit |
9795e1 |
r = LIBUSB_ERROR_INVALID_PARAM;
|
|
Packit |
9795e1 |
free(data);
|
|
Packit |
9795e1 |
goto error;
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
libusb_fill_bulk_transfer(transfer->transfer, host->handle, ep,
|
|
Packit |
9795e1 |
data, len, usbredirhost_bulk_packet_complete,
|
|
Packit |
9795e1 |
transfer, BULK_TIMEOUT);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
transfer->id = id;
|
|
Packit |
9795e1 |
transfer->bulk_packet = *bulk_packet;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_add_transfer(host, transfer);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_submit_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
#if LIBUSBX_API_VERSION < 0x01000103
|
|
Packit |
9795e1 |
error:
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
ERROR("error submitting bulk transfer on ep %02X: %s",
|
|
Packit |
9795e1 |
ep, libusb_error_name(r));
|
|
Packit |
9795e1 |
transfer->transfer->actual_length = 0;
|
|
Packit |
9795e1 |
transfer->transfer->status = r;
|
|
Packit |
9795e1 |
usbredirhost_bulk_packet_complete(transfer->transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_iso_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_iso_packet_header *iso_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = iso_packet->endpoint;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer;
|
|
Packit |
9795e1 |
int i, j, status = usb_redir_success;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
status = usb_redir_ioerror;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].type != usb_redir_type_iso) {
|
|
Packit |
9795e1 |
ERROR("error received iso packet for non iso ep %02X", ep);
|
|
Packit |
9795e1 |
status = usb_redir_inval;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].transfer_count == 0) {
|
|
Packit |
9795e1 |
ERROR("error received iso out packet for non started iso stream");
|
|
Packit |
9795e1 |
status = usb_redir_inval;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (data_len > host->endpoint[EP2I(ep)].max_packetsize) {
|
|
Packit |
9795e1 |
ERROR("error received iso out packet is larger than wMaxPacketSize");
|
|
Packit |
9795e1 |
status = usb_redir_inval;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].drop_packets) {
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].drop_packets--;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
i = host->endpoint[EP2I(ep)].out_idx;
|
|
Packit |
9795e1 |
transfer = host->endpoint[EP2I(ep)].transfer[i];
|
|
Packit |
9795e1 |
j = transfer->packet_idx;
|
|
Packit |
9795e1 |
if (j == SUBMITTED_IDX) {
|
|
Packit |
9795e1 |
DEBUG("overflow of iso out queue on ep: %02X, dropping packet", ep);
|
|
Packit |
9795e1 |
/* Since we're interupting the stream anyways, drop enough packets to
|
|
Packit |
9795e1 |
get back to our target buffer size */
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].drop_packets =
|
|
Packit |
9795e1 |
(host->endpoint[EP2I(ep)].pkts_per_transfer *
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer_count) / 2;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Store the id of the first packet in the urb */
|
|
Packit |
9795e1 |
if (j == 0) {
|
|
Packit |
9795e1 |
transfer->id = id;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
memcpy(libusb_get_iso_packet_buffer(transfer->transfer, j),
|
|
Packit |
9795e1 |
data, data_len);
|
|
Packit |
9795e1 |
transfer->transfer->iso_packet_desc[j].length = data_len;
|
|
Packit |
9795e1 |
DEBUG("iso-in queue ep %02X urb %d pkt %d len %d id %"PRIu64,
|
|
Packit |
9795e1 |
ep, i, j, data_len, transfer->id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
j++;
|
|
Packit |
9795e1 |
transfer->packet_idx = j;
|
|
Packit |
9795e1 |
if (j == host->endpoint[EP2I(ep)].pkts_per_transfer) {
|
|
Packit |
9795e1 |
i = (i + 1) % host->endpoint[EP2I(ep)].transfer_count;
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].out_idx = i;
|
|
Packit |
9795e1 |
j = 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].stream_started) {
|
|
Packit |
9795e1 |
if (transfer->packet_idx ==
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].pkts_per_transfer) {
|
|
Packit |
9795e1 |
usbredirhost_submit_stream_transfer_unlocked(host, transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
} else {
|
|
Packit |
9795e1 |
/* We've not started the stream (submitted some transfers) yet,
|
|
Packit |
9795e1 |
do so once we have half our buffers filled */
|
|
Packit |
9795e1 |
int available = i * host->endpoint[EP2I(ep)].pkts_per_transfer + j;
|
|
Packit |
9795e1 |
int needed = (host->endpoint[EP2I(ep)].pkts_per_transfer *
|
|
Packit |
9795e1 |
host->endpoint[EP2I(ep)].transfer_count) / 2;
|
|
Packit |
9795e1 |
if (available == needed) {
|
|
Packit |
9795e1 |
DEBUG("iso-in starting stream on ep %02X", ep);
|
|
Packit |
9795e1 |
usbredirhost_start_stream_unlocked(host, ep);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
leave:
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
if (status != usb_redir_success) {
|
|
Packit |
9795e1 |
usbredirhost_send_stream_status(host, id, ep, status);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void LIBUSB_CALL usbredirhost_interrupt_out_packet_complete(
|
|
Packit |
9795e1 |
struct libusb_transfer *libusb_transfer)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer = libusb_transfer->user_data;
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header interrupt_packet;
|
|
Packit |
9795e1 |
struct usbredirhost *host = transfer->host;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
LOCK(host);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
interrupt_packet = transfer->interrupt_packet;
|
|
Packit |
9795e1 |
interrupt_packet.status = libusb_status_or_error_to_redir_status(host,
|
|
Packit |
9795e1 |
libusb_transfer->status);
|
|
Packit |
9795e1 |
interrupt_packet.length = libusb_transfer->actual_length;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("interrupt out complete ep %02X status %d len %d id %"PRIu64,
|
|
Packit |
9795e1 |
interrupt_packet.endpoint, interrupt_packet.status,
|
|
Packit |
9795e1 |
interrupt_packet.length, transfer->id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (!transfer->cancelled) {
|
|
Packit |
9795e1 |
usbredirparser_send_interrupt_packet(host->parser, transfer->id,
|
|
Packit |
9795e1 |
&interrupt_packet, NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
usbredirhost_remove_and_free_transfer(transfer);
|
|
Packit |
9795e1 |
UNLOCK(host);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_send_interrupt_status(struct usbredirhost *host,
|
|
Packit |
9795e1 |
uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_packet,
|
|
Packit |
9795e1 |
uint8_t status)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
interrupt_packet->status = status;
|
|
Packit |
9795e1 |
interrupt_packet->length = 0;
|
|
Packit |
9795e1 |
usbredirparser_send_interrupt_packet(host->parser, id, interrupt_packet,
|
|
Packit |
9795e1 |
NULL, 0);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static void usbredirhost_interrupt_packet(void *priv, uint64_t id,
|
|
Packit |
9795e1 |
struct usb_redir_interrupt_packet_header *interrupt_packet,
|
|
Packit |
9795e1 |
uint8_t *data, int data_len)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
struct usbredirhost *host = priv;
|
|
Packit |
9795e1 |
uint8_t ep = interrupt_packet->endpoint;
|
|
Packit |
9795e1 |
struct usbredirtransfer *transfer;
|
|
Packit |
9795e1 |
int r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
DEBUG("interrupt submit ep %02X len %d id %"PRIu64, ep,
|
|
Packit |
9795e1 |
interrupt_packet->length, id);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->disconnected) {
|
|
Packit |
9795e1 |
usbredirhost_send_interrupt_status(host, id, interrupt_packet,
|
|
Packit |
9795e1 |
usb_redir_ioerror);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (host->endpoint[EP2I(ep)].type != usb_redir_type_interrupt) {
|
|
Packit |
9795e1 |
ERROR("error received interrupt packet for non interrupt ep %02X", ep);
|
|
Packit |
9795e1 |
usbredirhost_send_interrupt_status(host, id, interrupt_packet,
|
|
Packit |
9795e1 |
usb_redir_inval);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (data_len > host->endpoint[EP2I(ep)].max_packetsize) {
|
|
Packit |
9795e1 |
ERROR("error received interrupt out packet is larger than wMaxPacketSize");
|
|
Packit |
9795e1 |
usbredirhost_send_interrupt_status(host, id, interrupt_packet,
|
|
Packit |
9795e1 |
usb_redir_inval);
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
FLUSH(host);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_log_data(host, "interrupt data out:", data, data_len);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Note no memcpy, we can re-use the data buffer the parser
|
|
Packit |
9795e1 |
malloc-ed for us and expects us to free */
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
transfer = usbredirhost_alloc_transfer(host, 0);
|
|
Packit |
9795e1 |
if (!transfer) {
|
|
Packit |
9795e1 |
usbredirparser_free_packet_data(host->parser, data);
|
|
Packit |
9795e1 |
return;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
host->reset = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
libusb_fill_interrupt_transfer(transfer->transfer, host->handle, ep,
|
|
Packit |
9795e1 |
data, data_len, usbredirhost_interrupt_out_packet_complete,
|
|
Packit |
9795e1 |
transfer, INTERRUPT_TIMEOUT);
|
|
Packit |
9795e1 |
transfer->id = id;
|
|
Packit |
9795e1 |
transfer->interrupt_packet = *interrupt_packet;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
usbredirhost_add_transfer(host, transfer);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_submit_transfer(transfer->transfer);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
ERROR("error submitting interrupt transfer on ep %02X: %s",
|
|
Packit |
9795e1 |
ep, libusb_error_name(r));
|
|
Packit |
9795e1 |
transfer->transfer->actual_length = 0;
|
|
Packit |
9795e1 |
transfer->transfer->status = r;
|
|
Packit |
9795e1 |
usbredirhost_interrupt_out_packet_complete(transfer->transfer);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/**************************************************************************/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void usbredirhost_get_guest_filter(struct usbredirhost *host,
|
|
Packit |
9795e1 |
const struct usbredirfilter_rule **rules_ret, int *rules_count_ret)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
*rules_ret = host->filter_rules;
|
|
Packit |
9795e1 |
*rules_count_ret = host->filter_rules_count;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirhost_check_device_filter(const struct usbredirfilter_rule *rules,
|
|
Packit |
9795e1 |
int rules_count, libusb_device *dev, int flags)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, r, num_interfaces;
|
|
Packit |
9795e1 |
struct libusb_device_descriptor dev_desc;
|
|
Packit |
9795e1 |
struct libusb_config_descriptor *config = NULL;
|
|
Packit |
9795e1 |
uint8_t interface_class[MAX_INTERFACES];
|
|
Packit |
9795e1 |
uint8_t interface_subclass[MAX_INTERFACES];
|
|
Packit |
9795e1 |
uint8_t interface_protocol[MAX_INTERFACES];
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_get_device_descriptor(dev, &dev_desc);
|
|
Packit |
9795e1 |
if (r < 0) {
|
|
Packit |
9795e1 |
if (r == LIBUSB_ERROR_NO_MEM)
|
|
Packit |
9795e1 |
return -ENOMEM;
|
|
Packit |
9795e1 |
return -EIO;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
r = libusb_get_active_config_descriptor(dev, &config);
|
|
Packit |
9795e1 |
if (r < 0 && r != LIBUSB_ERROR_NOT_FOUND) {
|
|
Packit |
9795e1 |
if (r == LIBUSB_ERROR_NO_MEM)
|
|
Packit |
9795e1 |
return -ENOMEM;
|
|
Packit |
9795e1 |
return -EIO;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (config == NULL) {
|
|
Packit |
9795e1 |
return usbredirfilter_check(rules, rules_count, dev_desc.bDeviceClass,
|
|
Packit |
9795e1 |
dev_desc.bDeviceSubClass, dev_desc.bDeviceProtocol,
|
|
Packit |
9795e1 |
NULL, NULL, NULL, 0,
|
|
Packit |
9795e1 |
dev_desc.idVendor, dev_desc.idProduct,
|
|
Packit |
9795e1 |
dev_desc.bcdDevice, flags);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
num_interfaces = config->bNumInterfaces;
|
|
Packit |
9795e1 |
for (i = 0; i < num_interfaces; i++) {
|
|
Packit |
9795e1 |
const struct libusb_interface_descriptor *intf_desc =
|
|
Packit |
9795e1 |
config->interface[i].altsetting;
|
|
Packit |
9795e1 |
interface_class[i] = intf_desc->bInterfaceClass;
|
|
Packit |
9795e1 |
interface_subclass[i] = intf_desc->bInterfaceSubClass;
|
|
Packit |
9795e1 |
interface_protocol[i] = intf_desc->bInterfaceProtocol;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
libusb_free_config_descriptor(config);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return usbredirfilter_check(rules, rules_count, dev_desc.bDeviceClass,
|
|
Packit |
9795e1 |
dev_desc.bDeviceSubClass, dev_desc.bDeviceProtocol,
|
|
Packit |
9795e1 |
interface_class, interface_subclass, interface_protocol,
|
|
Packit |
9795e1 |
num_interfaces, dev_desc.idVendor, dev_desc.idProduct,
|
|
Packit |
9795e1 |
dev_desc.bcdDevice, flags);
|
|
Packit |
9795e1 |
}
|