2005-11-05 17:22:28 +03:00
|
|
|
/*
|
|
|
|
* Linux host USB redirector
|
|
|
|
*
|
|
|
|
* Copyright (c) 2005 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2008-08-21 23:31:10 +04:00
|
|
|
* Copyright (c) 2008 Max Krasnyansky
|
|
|
|
* Support for host device auto connect & disconnect
|
2008-09-14 05:07:41 +04:00
|
|
|
* Major rewrite to support fully async operation
|
2008-08-21 23:28:55 +04:00
|
|
|
*
|
2008-10-08 00:06:37 +04:00
|
|
|
* Copyright 2008 TJ <linux@tjworld.net>
|
|
|
|
* Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
|
|
|
|
* to the legacy /proc/bus/usb USB device discovery and handling
|
|
|
|
*
|
2005-11-05 17:22:28 +03:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "qemu-common.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/timer.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "monitor/monitor.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2011-08-22 16:13:20 +04:00
|
|
|
#include "trace.h"
|
2005-11-05 17:22:28 +03:00
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
2008-09-14 05:06:09 +04:00
|
|
|
#include <linux/usbdevice_fs.h>
|
|
|
|
#include <linux/version.h>
|
|
|
|
#include "hw/usb.h"
|
2012-03-29 18:06:28 +04:00
|
|
|
#include "hw/usb/desc.h"
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2008-09-15 18:57:11 +04:00
|
|
|
/* We redefine it to avoid version problems */
|
|
|
|
struct usb_ctrltransfer {
|
|
|
|
uint8_t bRequestType;
|
|
|
|
uint8_t bRequest;
|
|
|
|
uint16_t wValue;
|
|
|
|
uint16_t wIndex;
|
|
|
|
uint16_t wLength;
|
|
|
|
uint32_t timeout;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
2011-08-18 01:35:45 +04:00
|
|
|
typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
|
2010-11-10 12:06:23 +03:00
|
|
|
int class_id, int vendor_id, int product_id,
|
2005-11-06 19:13:29 +03:00
|
|
|
const char *product_name, int speed);
|
2009-10-26 17:56:50 +03:00
|
|
|
|
2009-11-27 15:05:53 +03:00
|
|
|
//#define DEBUG
|
2008-08-21 23:31:10 +04:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2010-02-07 02:03:50 +03:00
|
|
|
#define DPRINTF printf
|
2008-08-21 23:31:10 +04:00
|
|
|
#else
|
2010-02-07 02:03:50 +03:00
|
|
|
#define DPRINTF(...)
|
2008-08-21 23:31:10 +04:00
|
|
|
#endif
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2006-06-27 01:00:51 +04:00
|
|
|
#define PRODUCT_NAME_SZ 32
|
2011-05-10 13:43:57 +04:00
|
|
|
#define MAX_PORTLEN 16
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
/* endpoint association data */
|
2010-11-26 13:41:08 +03:00
|
|
|
#define ISO_FRAME_DESC_PER_URB 32
|
|
|
|
|
2011-05-16 12:21:51 +04:00
|
|
|
/* devio.c limits single requests to 16k */
|
|
|
|
#define MAX_USBFS_BUFFER_SIZE 16384
|
|
|
|
|
2010-11-26 13:41:08 +03:00
|
|
|
typedef struct AsyncURB AsyncURB;
|
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
struct endp_data {
|
2008-08-21 23:31:10 +04:00
|
|
|
uint8_t halted;
|
2010-11-26 21:11:03 +03:00
|
|
|
uint8_t iso_started;
|
2010-11-26 13:41:08 +03:00
|
|
|
AsyncURB *iso_urb;
|
|
|
|
int iso_urb_idx;
|
2010-11-26 21:11:03 +03:00
|
|
|
int iso_buffer_used;
|
2011-06-10 16:00:24 +04:00
|
|
|
int inflight;
|
2007-10-05 02:47:34 +04:00
|
|
|
};
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
struct USBAutoFilter {
|
|
|
|
uint32_t bus_num;
|
|
|
|
uint32_t addr;
|
2011-05-10 14:07:42 +04:00
|
|
|
char *port;
|
2009-10-26 17:56:50 +03:00
|
|
|
uint32_t vendor_id;
|
|
|
|
uint32_t product_id;
|
|
|
|
};
|
|
|
|
|
2012-03-22 18:28:45 +04:00
|
|
|
enum USBHostDeviceOptions {
|
|
|
|
USB_HOST_OPT_PIPELINE,
|
|
|
|
};
|
|
|
|
|
2005-11-05 17:22:28 +03:00
|
|
|
typedef struct USBHostDevice {
|
|
|
|
USBDevice dev;
|
2008-08-21 23:31:10 +04:00
|
|
|
int fd;
|
2011-08-24 15:34:17 +04:00
|
|
|
int hub_fd;
|
2012-01-05 18:49:18 +04:00
|
|
|
int hub_port;
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2011-05-31 13:35:26 +04:00
|
|
|
uint8_t descr[8192];
|
2008-08-21 23:31:10 +04:00
|
|
|
int descr_len;
|
2008-08-21 23:31:52 +04:00
|
|
|
int closing;
|
2011-06-10 16:03:56 +04:00
|
|
|
uint32_t iso_urb_count;
|
2012-03-22 18:28:45 +04:00
|
|
|
uint32_t options;
|
2010-06-16 16:16:11 +04:00
|
|
|
Notifier exit;
|
2012-06-08 15:02:16 +04:00
|
|
|
QEMUBH *bh;
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2011-08-29 14:49:46 +04:00
|
|
|
struct endp_data ep_in[USB_MAX_ENDPOINTS];
|
|
|
|
struct endp_data ep_out[USB_MAX_ENDPOINTS];
|
2011-05-16 11:13:05 +04:00
|
|
|
QLIST_HEAD(, AsyncURB) aurbs;
|
2008-08-21 23:28:55 +04:00
|
|
|
|
|
|
|
/* Host side address */
|
|
|
|
int bus_num;
|
|
|
|
int addr;
|
2011-05-10 13:43:57 +04:00
|
|
|
char port[MAX_PORTLEN];
|
2009-10-26 17:56:50 +03:00
|
|
|
struct USBAutoFilter match;
|
2012-03-22 13:48:03 +04:00
|
|
|
int32_t bootindex;
|
2011-08-24 15:45:06 +04:00
|
|
|
int seen, errcount;
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
QTAILQ_ENTRY(USBHostDevice) next;
|
2005-11-05 17:22:28 +03:00
|
|
|
} USBHostDevice;
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
|
|
|
|
|
|
|
|
static int usb_host_close(USBHostDevice *dev);
|
|
|
|
static int parse_filter(const char *spec, struct USBAutoFilter *f);
|
|
|
|
static void usb_host_auto_check(void *unused);
|
2010-11-10 12:06:25 +03:00
|
|
|
static int usb_host_read_file(char *line, size_t line_size,
|
|
|
|
const char *device_file, const char *device_name);
|
2012-11-15 12:23:30 +04:00
|
|
|
static void usb_linux_update_endp_table(USBHostDevice *s);
|
2009-10-26 17:56:50 +03:00
|
|
|
|
2011-08-29 14:49:46 +04:00
|
|
|
static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
|
|
|
|
{
|
|
|
|
static const int usbfs[] = {
|
|
|
|
[USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
|
|
|
|
[USB_ENDPOINT_XFER_ISOC] = USBDEVFS_URB_TYPE_ISO,
|
|
|
|
[USB_ENDPOINT_XFER_BULK] = USBDEVFS_URB_TYPE_BULK,
|
|
|
|
[USB_ENDPOINT_XFER_INT] = USBDEVFS_URB_TYPE_INTERRUPT,
|
|
|
|
};
|
2012-01-12 16:23:01 +04:00
|
|
|
uint8_t type = p->ep->type;
|
2011-08-29 14:49:46 +04:00
|
|
|
assert(type < ARRAY_SIZE(usbfs));
|
|
|
|
return usbfs[type];
|
|
|
|
}
|
|
|
|
|
2011-11-16 15:37:17 +04:00
|
|
|
static int usb_host_do_reset(USBHostDevice *dev)
|
|
|
|
{
|
|
|
|
struct timeval s, e;
|
|
|
|
uint32_t usecs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
gettimeofday(&s, NULL);
|
|
|
|
ret = ioctl(dev->fd, USBDEVFS_RESET);
|
|
|
|
gettimeofday(&e, NULL);
|
|
|
|
usecs = (e.tv_sec - s.tv_sec) * 1000000;
|
|
|
|
usecs += e.tv_usec - s.tv_usec;
|
|
|
|
if (usecs > 1000000) {
|
|
|
|
/* more than a second, something is fishy, broken usb device? */
|
|
|
|
fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
|
|
|
|
dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
|
2011-06-10 15:34:10 +04:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
|
|
|
|
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
|
2011-08-29 14:49:46 +04:00
|
|
|
assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
|
2011-08-26 18:27:41 +04:00
|
|
|
return eps + ep - 1;
|
2011-06-10 15:34:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int is_isoc(USBHostDevice *s, int pid, int ep)
|
2008-08-21 23:31:10 +04:00
|
|
|
{
|
2011-08-29 14:49:46 +04:00
|
|
|
return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
|
2008-08-21 23:31:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int is_valid(USBHostDevice *s, int pid, int ep)
|
2010-11-26 16:56:17 +03:00
|
|
|
{
|
2011-08-29 14:49:46 +04:00
|
|
|
return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
|
2010-11-26 16:56:17 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int is_halted(USBHostDevice *s, int pid, int ep)
|
2008-08-21 23:31:10 +04:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
return get_endp(s, pid, ep)->halted;
|
2008-08-21 23:31:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void clear_halt(USBHostDevice *s, int pid, int ep)
|
2008-08-21 23:31:10 +04:00
|
|
|
{
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
|
2011-08-26 18:27:41 +04:00
|
|
|
get_endp(s, pid, ep)->halted = 0;
|
2008-08-21 23:31:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void set_halt(USBHostDevice *s, int pid, int ep)
|
2008-08-21 23:31:10 +04:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
if (ep != 0) {
|
|
|
|
trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
|
|
|
|
get_endp(s, pid, ep)->halted = 1;
|
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int is_iso_started(USBHostDevice *s, int pid, int ep)
|
2010-11-26 21:11:03 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
return get_endp(s, pid, ep)->iso_started;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void clear_iso_started(USBHostDevice *s, int pid, int ep)
|
2010-11-26 21:11:03 +03:00
|
|
|
{
|
2012-07-03 17:43:49 +04:00
|
|
|
trace_usb_host_iso_stop(s->bus_num, s->addr, ep);
|
2011-08-26 18:27:41 +04:00
|
|
|
get_endp(s, pid, ep)->iso_started = 0;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void set_iso_started(USBHostDevice *s, int pid, int ep)
|
2010-11-26 21:11:03 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
struct endp_data *e = get_endp(s, pid, ep);
|
2011-08-22 16:13:20 +04:00
|
|
|
|
2012-07-03 17:43:49 +04:00
|
|
|
trace_usb_host_iso_start(s->bus_num, s->addr, ep);
|
2011-06-10 16:00:24 +04:00
|
|
|
if (!e->iso_started) {
|
|
|
|
e->iso_started = 1;
|
|
|
|
e->inflight = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
|
2011-06-10 16:00:24 +04:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
struct endp_data *e = get_endp(s, pid, ep);
|
2011-06-10 16:00:24 +04:00
|
|
|
|
|
|
|
e->inflight += value;
|
|
|
|
return e->inflight;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
get_endp(s, pid, ep)->iso_urb = iso_urb;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
return get_endp(s, pid, ep)->iso_urb;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
get_endp(s, pid, ep)->iso_urb_idx = i;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
return get_endp(s, pid, ep)->iso_urb_idx;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
|
2010-11-26 21:11:03 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
get_endp(s, pid, ep)->iso_buffer_used = i;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
|
2010-11-26 21:11:03 +03:00
|
|
|
{
|
2011-08-26 18:27:41 +04:00
|
|
|
return get_endp(s, pid, ep)->iso_buffer_used;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
/*
|
2008-08-21 23:31:10 +04:00
|
|
|
* Async URB state.
|
2010-11-26 13:41:08 +03:00
|
|
|
* We always allocate iso packet descriptors even for bulk transfers
|
2010-04-24 20:26:22 +04:00
|
|
|
* to simplify allocation and casts.
|
2008-08-21 23:31:10 +04:00
|
|
|
*/
|
2010-11-26 13:41:08 +03:00
|
|
|
struct AsyncURB
|
2008-08-21 23:31:10 +04:00
|
|
|
{
|
|
|
|
struct usbdevfs_urb urb;
|
2010-11-26 13:41:08 +03:00
|
|
|
struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
|
2011-05-16 11:13:05 +04:00
|
|
|
USBHostDevice *hdev;
|
|
|
|
QLIST_ENTRY(AsyncURB) next;
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2010-11-26 13:41:08 +03:00
|
|
|
/* For regular async urbs */
|
2008-08-21 23:31:10 +04:00
|
|
|
USBPacket *packet;
|
2011-05-16 12:21:51 +04:00
|
|
|
int more; /* large transfer, more urbs follow */
|
2010-11-26 13:41:08 +03:00
|
|
|
|
|
|
|
/* For buffered iso handling */
|
|
|
|
int iso_frame_idx; /* -1 means in flight */
|
|
|
|
};
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-16 11:13:05 +04:00
|
|
|
static AsyncURB *async_alloc(USBHostDevice *s)
|
2007-10-05 02:47:34 +04:00
|
|
|
{
|
2011-08-21 07:09:37 +04:00
|
|
|
AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
|
2011-05-16 11:13:05 +04:00
|
|
|
aurb->hdev = s;
|
|
|
|
QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
|
|
|
|
return aurb;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
|
2008-08-21 23:31:10 +04:00
|
|
|
static void async_free(AsyncURB *aurb)
|
2007-10-05 02:47:34 +04:00
|
|
|
{
|
2011-05-16 11:13:05 +04:00
|
|
|
QLIST_REMOVE(aurb, next);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(aurb);
|
2008-08-21 23:31:10 +04:00
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-24 18:12:31 +04:00
|
|
|
static void do_disconnect(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
usb_host_close(s);
|
|
|
|
usb_host_auto_check(NULL);
|
|
|
|
}
|
|
|
|
|
2008-08-21 23:31:10 +04:00
|
|
|
static void async_complete(void *opaque)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = opaque;
|
|
|
|
AsyncURB *aurb;
|
2011-06-10 16:00:24 +04:00
|
|
|
int urbs = 0;
|
2008-08-21 23:31:10 +04:00
|
|
|
|
|
|
|
while (1) {
|
2010-04-24 20:26:22 +04:00
|
|
|
USBPacket *p;
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
|
2008-08-21 23:31:10 +04:00
|
|
|
if (r < 0) {
|
2010-04-24 20:26:22 +04:00
|
|
|
if (errno == EAGAIN) {
|
2011-06-10 16:00:24 +04:00
|
|
|
if (urbs > 2) {
|
2012-07-03 17:43:49 +04:00
|
|
|
/* indicates possible latency issues */
|
|
|
|
trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs);
|
2011-06-10 16:00:24 +04:00
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-08-22 16:18:21 +04:00
|
|
|
if (errno == ENODEV) {
|
|
|
|
if (!s->closing) {
|
|
|
|
trace_usb_host_disconnect(s->bus_num, s->addr);
|
|
|
|
do_disconnect(s);
|
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_REAPURBNDELAY");
|
2008-08-21 23:31:10 +04:00
|
|
|
return;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
|
2008-08-21 23:31:10 +04:00
|
|
|
aurb, aurb->urb.status, aurb->urb.actual_length);
|
|
|
|
|
2010-11-26 13:41:08 +03:00
|
|
|
/* If this is a buffered iso urb mark it as complete and don't do
|
|
|
|
anything else (it is handled further in usb_host_handle_iso_data) */
|
|
|
|
if (aurb->iso_frame_idx == -1) {
|
2011-06-10 16:00:24 +04:00
|
|
|
int inflight;
|
2011-08-26 18:27:41 +04:00
|
|
|
int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
|
|
|
|
USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
int ep = aurb->urb.endpoint & 0xf;
|
2010-11-26 13:41:08 +03:00
|
|
|
if (aurb->urb.status == -EPIPE) {
|
2011-08-26 18:27:41 +04:00
|
|
|
set_halt(s, pid, ep);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
aurb->iso_frame_idx = 0;
|
2011-06-10 16:00:24 +04:00
|
|
|
urbs++;
|
2011-08-26 18:27:41 +04:00
|
|
|
inflight = change_iso_inflight(s, pid, ep, -1);
|
|
|
|
if (inflight == 0 && is_iso_started(s, pid, ep)) {
|
2012-07-03 17:43:49 +04:00
|
|
|
/* can be latency issues, or simply end of stream */
|
|
|
|
trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep);
|
2011-06-10 16:00:24 +04:00
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = aurb->packet;
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
|
|
|
|
aurb->urb.actual_length, aurb->more);
|
2010-11-26 13:41:08 +03:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (p) {
|
2008-08-21 23:31:10 +04:00
|
|
|
switch (aurb->urb.status) {
|
|
|
|
case 0:
|
2012-11-15 19:11:49 +04:00
|
|
|
p->actual_length += aurb->urb.actual_length;
|
|
|
|
if (!aurb->more) {
|
|
|
|
/* Clear previous ASYNC status */
|
|
|
|
p->status = USB_RET_SUCCESS;
|
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case -EPIPE:
|
2012-01-12 16:23:01 +04:00
|
|
|
set_halt(s, p->pid, p->ep->nr);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
2010-04-24 20:26:22 +04:00
|
|
|
break;
|
2009-10-13 15:40:08 +04:00
|
|
|
|
2012-03-03 00:27:19 +04:00
|
|
|
case -EOVERFLOW:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_BABBLE;
|
2012-03-03 00:27:19 +04:00
|
|
|
break;
|
|
|
|
|
2008-08-21 23:31:10 +04:00
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_IOERROR;
|
2008-08-21 23:31:10 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, aurb->urb.actual_length);
|
2011-02-02 19:36:29 +03:00
|
|
|
usb_generic_async_ctrl_complete(&s->dev, p);
|
2011-05-16 12:21:51 +04:00
|
|
|
} else if (!aurb->more) {
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, aurb->urb.actual_length);
|
2011-02-02 19:36:29 +03:00
|
|
|
usb_packet_complete(&s->dev, p);
|
|
|
|
}
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
|
|
|
|
async_free(aurb);
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-16 12:34:53 +04:00
|
|
|
static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
|
2007-10-05 02:47:34 +04:00
|
|
|
{
|
2011-05-16 12:34:53 +04:00
|
|
|
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
2011-05-16 11:20:06 +04:00
|
|
|
AsyncURB *aurb;
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2012-03-23 15:35:55 +04:00
|
|
|
trace_usb_host_req_canceled(s->bus_num, s->addr, p);
|
2012-03-23 15:26:59 +04:00
|
|
|
|
2011-05-16 11:20:06 +04:00
|
|
|
QLIST_FOREACH(aurb, &s->aurbs, next) {
|
|
|
|
if (p != aurb->packet) {
|
|
|
|
continue;
|
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2012-03-23 15:26:59 +04:00
|
|
|
trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-16 11:20:06 +04:00
|
|
|
/* Mark it as dead (see async_complete above) */
|
|
|
|
aurb->packet = NULL;
|
|
|
|
|
|
|
|
int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
|
|
|
|
if (r < 0) {
|
|
|
|
DPRINTF("husb: async. discard urb failed errno %d\n", errno);
|
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 14:54:11 +04:00
|
|
|
static int usb_host_open_device(int bus, int addr)
|
|
|
|
{
|
|
|
|
const char *usbfs = NULL;
|
|
|
|
char filename[32];
|
|
|
|
struct stat st;
|
|
|
|
int fd, rc;
|
|
|
|
|
|
|
|
rc = stat("/dev/bus/usb", &st);
|
|
|
|
if (rc == 0 && S_ISDIR(st.st_mode)) {
|
|
|
|
/* udev-created device nodes available */
|
|
|
|
usbfs = "/dev/bus/usb";
|
|
|
|
} else {
|
|
|
|
/* fallback: usbfs mounted below /proc */
|
|
|
|
usbfs = "/proc/bus/usb";
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%03d/%03d",
|
|
|
|
usbfs, bus, addr);
|
|
|
|
fd = open(filename, O_RDWR | O_NONBLOCK);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2011-09-13 13:37:47 +04:00
|
|
|
static int usb_host_claim_port(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
#ifdef USBDEVFS_CLAIM_PORT
|
|
|
|
char *h, hub_name[64], line[1024];
|
2012-01-05 18:49:18 +04:00
|
|
|
int hub_addr, ret;
|
2011-09-13 13:37:47 +04:00
|
|
|
|
|
|
|
snprintf(hub_name, sizeof(hub_name), "%d-%s",
|
|
|
|
s->match.bus_num, s->match.port);
|
|
|
|
|
|
|
|
/* try strip off last ".$portnr" to get hub */
|
|
|
|
h = strrchr(hub_name, '.');
|
|
|
|
if (h != NULL) {
|
2012-01-05 18:49:18 +04:00
|
|
|
s->hub_port = atoi(h+1);
|
2011-09-13 13:37:47 +04:00
|
|
|
*h = '\0';
|
|
|
|
} else {
|
|
|
|
/* no dot in there -> it is the root hub */
|
|
|
|
snprintf(hub_name, sizeof(hub_name), "usb%d",
|
|
|
|
s->match.bus_num);
|
2012-01-05 18:49:18 +04:00
|
|
|
s->hub_port = atoi(s->match.port);
|
2011-09-13 13:37:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!usb_host_read_file(line, sizeof(line), "devnum",
|
|
|
|
hub_name)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (sscanf(line, "%d", &hub_addr) != 1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-12-16 14:54:11 +04:00
|
|
|
s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
|
2011-09-13 13:37:47 +04:00
|
|
|
if (s->hub_fd < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-01-05 18:49:18 +04:00
|
|
|
ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
|
2011-09-13 13:37:47 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
close(s->hub_fd);
|
|
|
|
s->hub_fd = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-01-05 18:49:18 +04:00
|
|
|
trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
|
2011-09-13 13:37:47 +04:00
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-01-05 18:49:18 +04:00
|
|
|
static void usb_host_release_port(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
if (s->hub_fd == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#ifdef USBDEVFS_RELEASE_PORT
|
|
|
|
ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
|
|
|
|
#endif
|
|
|
|
close(s->hub_fd);
|
|
|
|
s->hub_fd = -1;
|
|
|
|
}
|
|
|
|
|
2011-09-13 13:37:47 +04:00
|
|
|
static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
|
|
|
|
{
|
|
|
|
/* earlier Linux 2.4 do not support that */
|
|
|
|
#ifdef USBDEVFS_DISCONNECT
|
|
|
|
struct usbdevfs_ioctl ctrl;
|
|
|
|
int ret, interface;
|
|
|
|
|
|
|
|
for (interface = 0; interface < nb_interfaces; interface++) {
|
|
|
|
ctrl.ioctl_code = USBDEVFS_DISCONNECT;
|
|
|
|
ctrl.ifno = interface;
|
|
|
|
ctrl.data = 0;
|
|
|
|
ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
|
|
|
|
if (ret < 0 && errno != ENODATA) {
|
|
|
|
perror("USBDEVFS_DISCONNECT");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-13 13:55:15 +04:00
|
|
|
static int usb_linux_get_num_interfaces(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
char device_name[64], line[1024];
|
|
|
|
int num_interfaces = 0;
|
|
|
|
|
|
|
|
sprintf(device_name, "%d-%s", s->bus_num, s->port);
|
|
|
|
if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
|
|
|
|
device_name)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (sscanf(line, "%d", &num_interfaces) != 1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return num_interfaces;
|
|
|
|
}
|
|
|
|
|
2008-09-14 05:06:09 +04:00
|
|
|
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
2007-10-05 02:47:34 +04:00
|
|
|
{
|
2011-05-24 18:12:31 +04:00
|
|
|
const char *op = NULL;
|
2007-10-05 02:47:34 +04:00
|
|
|
int dev_descr_len, config_descr_len;
|
2010-04-25 22:23:04 +04:00
|
|
|
int interface, nb_interfaces;
|
2007-10-05 02:47:34 +04:00
|
|
|
int ret, i;
|
|
|
|
|
2011-08-30 15:21:27 +04:00
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
|
|
|
dev->dev.altsetting[i] = 0;
|
|
|
|
}
|
|
|
|
|
2011-08-24 16:45:07 +04:00
|
|
|
if (configuration == 0) { /* address state - ignore */
|
2011-08-30 13:11:29 +04:00
|
|
|
dev->dev.ninterfaces = 0;
|
|
|
|
dev->dev.configuration = 0;
|
2007-10-05 02:47:34 +04:00
|
|
|
return 1;
|
2011-08-24 16:45:07 +04:00
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: claiming interfaces. config %d\n", configuration);
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
i = 0;
|
|
|
|
dev_descr_len = dev->descr[0];
|
2010-04-24 20:26:22 +04:00
|
|
|
if (dev_descr_len > dev->descr_len) {
|
2011-05-31 13:35:20 +04:00
|
|
|
fprintf(stderr, "husb: update iface failed. descr too short\n");
|
|
|
|
return 0;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
|
|
|
i += dev_descr_len;
|
|
|
|
while (i < dev->descr_len) {
|
2010-04-24 20:26:22 +04:00
|
|
|
DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
|
|
|
|
i, dev->descr_len,
|
2007-10-05 02:47:34 +04:00
|
|
|
dev->descr[i], dev->descr[i+1]);
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
if (dev->descr[i+1] != USB_DT_CONFIG) {
|
|
|
|
i += dev->descr[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
config_descr_len = dev->descr[i];
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
|
2008-08-21 23:27:48 +04:00
|
|
|
|
2011-08-24 16:45:07 +04:00
|
|
|
if (configuration == dev->descr[i + 5]) {
|
2008-09-14 05:06:09 +04:00
|
|
|
configuration = dev->descr[i + 5];
|
2007-10-05 02:47:34 +04:00
|
|
|
break;
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
|
|
|
i += config_descr_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= dev->descr_len) {
|
2010-04-24 20:26:22 +04:00
|
|
|
fprintf(stderr,
|
|
|
|
"husb: update iface failed. no matching configuration\n");
|
2011-05-31 13:35:20 +04:00
|
|
|
return 0;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
nb_interfaces = dev->descr[i + 4];
|
|
|
|
|
2011-09-13 13:37:47 +04:00
|
|
|
if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
|
|
|
|
goto fail;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: only grab if all interfaces are free */
|
|
|
|
for (interface = 0; interface < nb_interfaces; interface++) {
|
2011-05-24 18:12:31 +04:00
|
|
|
op = "USBDEVFS_CLAIMINTERFACE";
|
2007-10-05 02:47:34 +04:00
|
|
|
ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
|
|
|
|
if (ret < 0) {
|
2011-05-24 18:12:31 +04:00
|
|
|
goto fail;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
|
|
|
|
nb_interfaces, configuration);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-08-30 13:11:29 +04:00
|
|
|
dev->dev.ninterfaces = nb_interfaces;
|
|
|
|
dev->dev.configuration = configuration;
|
2008-09-14 05:06:09 +04:00
|
|
|
return 1;
|
2011-05-24 18:12:31 +04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
if (errno == ENODEV) {
|
|
|
|
do_disconnect(dev);
|
|
|
|
}
|
|
|
|
perror(op);
|
|
|
|
return 0;
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_release_interfaces(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
int ret, i;
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_release_interfaces(s->bus_num, s->addr);
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-08-30 13:11:29 +04:00
|
|
|
for (i = 0; i < s->dev.ninterfaces; i++) {
|
2008-09-14 05:06:09 +04:00
|
|
|
ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
|
|
|
|
if (ret < 0) {
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_RELEASEINTERFACE");
|
2008-09-14 05:06:09 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-07-19 22:06:15 +04:00
|
|
|
static void usb_host_handle_reset(USBDevice *dev)
|
2005-11-05 17:22:28 +03:00
|
|
|
{
|
2009-10-26 17:56:50 +03:00
|
|
|
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_reset(s->bus_num, s->addr);
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2011-11-16 15:37:17 +04:00
|
|
|
usb_host_do_reset(s);;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-08-24 16:45:07 +04:00
|
|
|
usb_host_claim_interfaces(s, 0);
|
2011-08-24 12:55:40 +04:00
|
|
|
usb_linux_update_endp_table(s);
|
2007-09-17 01:08:06 +04:00
|
|
|
}
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2006-07-19 22:06:15 +04:00
|
|
|
static void usb_host_handle_destroy(USBDevice *dev)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = (USBHostDevice *)dev;
|
|
|
|
|
2012-01-05 18:49:18 +04:00
|
|
|
usb_host_release_port(s);
|
2009-10-26 17:56:50 +03:00
|
|
|
usb_host_close(s);
|
|
|
|
QTAILQ_REMOVE(&hostdevs, s, next);
|
2010-06-16 16:16:11 +04:00
|
|
|
qemu_remove_exit_notifier(&s->exit);
|
2006-07-19 22:06:15 +04:00
|
|
|
}
|
|
|
|
|
2010-11-26 13:41:08 +03:00
|
|
|
/* iso data is special, we need to keep enough urbs in flight to make sure
|
|
|
|
that the controller never runs out of them, otherwise the device will
|
|
|
|
likely suffer a buffer underrun / overrun. */
|
2011-08-26 18:27:41 +04:00
|
|
|
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
|
|
|
AsyncURB *aurb;
|
2011-08-31 18:09:27 +04:00
|
|
|
int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
|
2010-11-26 13:41:08 +03:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
|
2011-06-10 16:03:56 +04:00
|
|
|
for (i = 0; i < s->iso_urb_count; i++) {
|
2010-11-26 13:41:08 +03:00
|
|
|
aurb[i].urb.endpoint = ep;
|
|
|
|
aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
|
2011-08-21 07:09:37 +04:00
|
|
|
aurb[i].urb.buffer = g_malloc(aurb[i].urb.buffer_length);
|
2010-11-26 13:41:08 +03:00
|
|
|
aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO;
|
|
|
|
aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP;
|
|
|
|
aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
|
|
|
|
for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
|
|
|
|
aurb[i].urb.iso_frame_desc[j].length = len;
|
2011-08-26 18:27:41 +04:00
|
|
|
if (pid == USB_TOKEN_IN) {
|
2010-11-26 13:41:08 +03:00
|
|
|
aurb[i].urb.endpoint |= 0x80;
|
|
|
|
/* Mark as fully consumed (idle) */
|
|
|
|
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
|
|
|
|
}
|
|
|
|
}
|
2011-08-26 18:27:41 +04:00
|
|
|
set_iso_urb(s, pid, ep, aurb);
|
2010-11-26 13:41:08 +03:00
|
|
|
|
|
|
|
return aurb;
|
|
|
|
}
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
|
|
|
AsyncURB *aurb;
|
|
|
|
int i, ret, killed = 0, free = 1;
|
|
|
|
|
2011-08-26 18:27:41 +04:00
|
|
|
aurb = get_iso_urb(s, pid, ep);
|
2010-11-26 13:41:08 +03:00
|
|
|
if (!aurb) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-10 16:03:56 +04:00
|
|
|
for (i = 0; i < s->iso_urb_count; i++) {
|
2010-11-26 13:41:08 +03:00
|
|
|
/* in flight? */
|
|
|
|
if (aurb[i].iso_frame_idx == -1) {
|
|
|
|
ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
|
|
|
|
if (ret < 0) {
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_DISCARDURB");
|
2010-11-26 13:41:08 +03:00
|
|
|
free = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
killed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure any urbs we've killed are reaped before we free them */
|
|
|
|
if (killed) {
|
|
|
|
async_complete(s);
|
|
|
|
}
|
|
|
|
|
2011-06-10 16:03:56 +04:00
|
|
|
for (i = 0; i < s->iso_urb_count; i++) {
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(aurb[i].urb.buffer);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (free)
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(aurb);
|
2010-11-26 13:41:08 +03:00
|
|
|
else
|
|
|
|
printf("husb: leaking iso urbs because of discard failure\n");
|
2011-08-26 18:27:41 +04:00
|
|
|
set_iso_urb(s, pid, ep, NULL);
|
|
|
|
set_iso_urb_idx(s, pid, ep, 0);
|
|
|
|
clear_iso_started(s, pid, ep);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void urb_status_to_usb_ret(int status, USBPacket *p)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case -EPIPE:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
break;
|
2012-03-03 00:27:19 +04:00
|
|
|
case -EOVERFLOW:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_BABBLE;
|
|
|
|
break;
|
2010-11-26 13:41:08 +03:00
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_IOERROR;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
2010-11-26 13:41:08 +03:00
|
|
|
{
|
|
|
|
AsyncURB *aurb;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
int i, j, max_packet_size, offset, len;
|
2011-07-12 17:22:25 +04:00
|
|
|
uint8_t *buf;
|
2010-11-26 16:59:35 +03:00
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
max_packet_size = p->ep->max_packet_size;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
if (max_packet_size == 0) {
|
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
return;
|
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
aurb = get_iso_urb(s, p->pid, p->ep->nr);
|
2010-11-26 13:41:08 +03:00
|
|
|
if (!aurb) {
|
2012-01-12 16:23:01 +04:00
|
|
|
aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
i = get_iso_urb_idx(s, p->pid, p->ep->nr);
|
2010-11-26 13:41:08 +03:00
|
|
|
j = aurb[i].iso_frame_idx;
|
|
|
|
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
|
2010-11-26 21:11:03 +03:00
|
|
|
if (in) {
|
|
|
|
/* Check urb status */
|
|
|
|
if (aurb[i].urb.status) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
urb_status_to_usb_ret(aurb[i].urb.status, p);
|
2010-11-26 21:11:03 +03:00
|
|
|
/* Move to the next urb */
|
|
|
|
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
|
|
|
|
/* Check frame status */
|
|
|
|
} else if (aurb[i].urb.iso_frame_desc[j].status) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
|
2010-11-26 21:11:03 +03:00
|
|
|
/* Check the frame fits */
|
2011-07-12 17:22:25 +04:00
|
|
|
} else if (aurb[i].urb.iso_frame_desc[j].actual_length
|
|
|
|
> p->iov.size) {
|
2010-11-26 21:11:03 +03:00
|
|
|
printf("husb: received iso data is larger then packet\n");
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_BABBLE;
|
2010-11-26 21:11:03 +03:00
|
|
|
/* All good copy data over */
|
|
|
|
} else {
|
|
|
|
len = aurb[i].urb.iso_frame_desc[j].actual_length;
|
2011-07-12 17:22:25 +04:00
|
|
|
buf = aurb[i].urb.buffer +
|
|
|
|
j * aurb[i].urb.iso_frame_desc[0].length;
|
|
|
|
usb_packet_copy(p, buf, len);
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
} else {
|
2011-07-12 17:22:25 +04:00
|
|
|
len = p->iov.size;
|
2012-01-12 16:23:01 +04:00
|
|
|
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
|
2010-11-26 21:11:03 +03:00
|
|
|
|
|
|
|
/* Check the frame fits */
|
|
|
|
if (len > max_packet_size) {
|
|
|
|
printf("husb: send iso data is larger then max packet size\n");
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
return;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All good copy data over */
|
2011-07-12 17:22:25 +04:00
|
|
|
usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
|
2010-11-26 21:11:03 +03:00
|
|
|
aurb[i].urb.iso_frame_desc[j].length = len;
|
|
|
|
offset += len;
|
2012-01-12 16:23:01 +04:00
|
|
|
set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
|
2010-11-26 21:11:03 +03:00
|
|
|
|
|
|
|
/* Start the stream once we have buffered enough data */
|
2012-01-12 16:23:01 +04:00
|
|
|
if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
|
|
|
|
set_iso_started(s, p->pid, p->ep->nr);
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
aurb[i].iso_frame_idx++;
|
|
|
|
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
2011-06-10 16:03:56 +04:00
|
|
|
i = (i + 1) % s->iso_urb_count;
|
2012-01-12 16:23:01 +04:00
|
|
|
set_iso_urb_idx(s, p->pid, p->ep->nr, i);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
2010-11-26 21:11:03 +03:00
|
|
|
} else {
|
|
|
|
if (in) {
|
2012-01-12 16:23:01 +04:00
|
|
|
set_iso_started(s, p->pid, p->ep->nr);
|
2010-11-26 21:11:03 +03:00
|
|
|
} else {
|
|
|
|
DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
|
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
if (is_iso_started(s, p->pid, p->ep->nr)) {
|
2010-11-26 21:11:03 +03:00
|
|
|
/* (Re)-submit all fully consumed / filled urbs */
|
2011-06-10 16:03:56 +04:00
|
|
|
for (i = 0; i < s->iso_urb_count; i++) {
|
2010-11-26 21:11:03 +03:00
|
|
|
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_SUBMITURB");
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
if (!in || p->status == USB_RET_SUCCESS) {
|
2010-11-26 21:11:03 +03:00
|
|
|
switch(errno) {
|
|
|
|
case ETIMEDOUT:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
2011-05-08 00:10:53 +04:00
|
|
|
break;
|
2010-11-26 21:11:03 +03:00
|
|
|
case EPIPE:
|
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
2010-11-26 21:11:03 +03:00
|
|
|
break;
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
2010-11-26 21:11:03 +03:00
|
|
|
aurb[i].iso_frame_idx = -1;
|
2012-01-12 16:23:01 +04:00
|
|
|
change_iso_inflight(s, p->pid, p->ep->nr, 1);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
2005-11-05 17:22:28 +03:00
|
|
|
{
|
2011-02-02 19:36:29 +03:00
|
|
|
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
2008-08-21 23:31:10 +04:00
|
|
|
struct usbdevfs_urb *urb;
|
2008-09-14 05:06:09 +04:00
|
|
|
AsyncURB *aurb;
|
2011-07-13 13:28:17 +04:00
|
|
|
int ret, rem, prem, v;
|
2011-05-16 12:21:51 +04:00
|
|
|
uint8_t *pbuf;
|
2010-11-26 13:41:08 +03:00
|
|
|
uint8_t ep;
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2012-03-23 15:35:55 +04:00
|
|
|
trace_usb_host_req_data(s->bus_num, s->addr, p,
|
2011-08-22 16:13:20 +04:00
|
|
|
p->pid == USB_TOKEN_IN,
|
2012-01-12 16:23:01 +04:00
|
|
|
p->ep->nr, p->iov.size);
|
2011-08-22 16:13:20 +04:00
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
if (!is_valid(s, p->pid, p->ep->nr)) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2010-11-26 16:56:17 +03:00
|
|
|
}
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (p->pid == USB_TOKEN_IN) {
|
2012-01-12 16:23:01 +04:00
|
|
|
ep = p->ep->nr | 0x80;
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2012-01-12 16:23:01 +04:00
|
|
|
ep = p->ep->nr;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-08-21 23:31:10 +04:00
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
if (is_halted(s, p->pid, p->ep->nr)) {
|
2011-08-24 12:55:40 +04:00
|
|
|
unsigned int arg = ep;
|
|
|
|
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
|
2008-08-21 23:31:10 +04:00
|
|
|
if (ret < 0) {
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_CLEAR_HALT");
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2005-11-05 17:22:28 +03:00
|
|
|
}
|
2012-01-12 16:23:01 +04:00
|
|
|
clear_halt(s, p->pid, p->ep->nr);
|
2007-10-05 02:55:53 +04:00
|
|
|
}
|
|
|
|
|
2012-01-12 16:23:01 +04:00
|
|
|
if (is_isoc(s, p->pid, p->ep->nr)) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
|
|
|
|
return;
|
2010-11-26 21:11:03 +03:00
|
|
|
}
|
2010-11-26 13:41:08 +03:00
|
|
|
|
2011-07-13 13:28:17 +04:00
|
|
|
v = 0;
|
2012-04-19 15:35:07 +04:00
|
|
|
prem = 0;
|
|
|
|
pbuf = NULL;
|
2011-07-13 13:28:17 +04:00
|
|
|
rem = p->iov.size;
|
2012-04-19 15:36:40 +04:00
|
|
|
do {
|
|
|
|
if (prem == 0 && rem > 0) {
|
2011-07-13 13:28:17 +04:00
|
|
|
assert(v < p->iov.niov);
|
|
|
|
prem = p->iov.iov[v].iov_len;
|
|
|
|
pbuf = p->iov.iov[v].iov_base;
|
|
|
|
assert(prem <= rem);
|
2012-04-19 15:35:07 +04:00
|
|
|
v++;
|
2011-07-13 13:28:17 +04:00
|
|
|
}
|
2011-05-16 12:21:51 +04:00
|
|
|
aurb = async_alloc(s);
|
|
|
|
aurb->packet = p;
|
|
|
|
|
|
|
|
urb = &aurb->urb;
|
|
|
|
urb->endpoint = ep;
|
2011-08-29 14:49:46 +04:00
|
|
|
urb->type = usb_host_usbfs_type(s, p);
|
2011-05-16 12:21:51 +04:00
|
|
|
urb->usercontext = s;
|
|
|
|
urb->buffer = pbuf;
|
2011-07-13 13:28:17 +04:00
|
|
|
urb->buffer_length = prem;
|
2011-05-16 12:21:51 +04:00
|
|
|
|
2011-07-13 13:28:17 +04:00
|
|
|
if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
|
2011-05-16 12:21:51 +04:00
|
|
|
urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
pbuf += urb->buffer_length;
|
2011-07-13 13:28:17 +04:00
|
|
|
prem -= urb->buffer_length;
|
2011-05-16 12:21:51 +04:00
|
|
|
rem -= urb->buffer_length;
|
2011-07-13 13:28:17 +04:00
|
|
|
if (rem) {
|
|
|
|
aurb->more = 1;
|
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
|
|
|
|
urb->buffer_length, aurb->more);
|
2011-05-16 12:21:51 +04:00
|
|
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-16 12:21:51 +04:00
|
|
|
DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
|
|
|
|
urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-16 12:21:51 +04:00
|
|
|
if (ret < 0) {
|
2011-08-22 16:13:20 +04:00
|
|
|
perror("USBDEVFS_SUBMITURB");
|
2011-05-16 12:21:51 +04:00
|
|
|
async_free(aurb);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-16 12:21:51 +04:00
|
|
|
switch(errno) {
|
|
|
|
case ETIMEDOUT:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
break;
|
2011-05-16 12:21:51 +04:00
|
|
|
case EPIPE:
|
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
2012-11-15 19:11:20 +04:00
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
2011-05-16 12:21:51 +04:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
2012-04-19 15:36:40 +04:00
|
|
|
} while (rem > 0);
|
2008-08-21 23:31:10 +04:00
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_ASYNC;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
|
2008-09-14 05:06:09 +04:00
|
|
|
static int ctrl_error(void)
|
|
|
|
{
|
2010-04-24 20:26:22 +04:00
|
|
|
if (errno == ETIMEDOUT) {
|
2008-09-14 05:06:09 +04:00
|
|
|
return USB_RET_NAK;
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2008-09-14 05:06:09 +04:00
|
|
|
return USB_RET_STALL;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_set_address(USBHostDevice *s, int addr)
|
2008-09-14 05:06:09 +04:00
|
|
|
{
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_set_address(s->bus_num, s->addr, addr);
|
2008-09-14 05:06:09 +04:00
|
|
|
s->dev.addr = addr;
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
|
2008-09-14 05:06:09 +04:00
|
|
|
{
|
2011-09-13 13:55:15 +04:00
|
|
|
int ret, first = 1;
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_set_config(s->bus_num, s->addr, config);
|
|
|
|
|
2008-09-14 05:06:09 +04:00
|
|
|
usb_host_release_interfaces(s);
|
|
|
|
|
2011-09-13 13:55:15 +04:00
|
|
|
again:
|
|
|
|
ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
|
2010-04-24 20:26:22 +04:00
|
|
|
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
|
2010-04-24 20:26:22 +04:00
|
|
|
|
2011-09-13 13:55:15 +04:00
|
|
|
if (ret < 0 && errno == EBUSY && first) {
|
|
|
|
/* happens if usb device is in use by host drivers */
|
|
|
|
int count = usb_linux_get_num_interfaces(s);
|
|
|
|
if (count > 0) {
|
|
|
|
DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
|
|
|
|
usb_host_disconnect_ifaces(s, count);
|
|
|
|
first = 0;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (ret < 0) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = ctrl_error();
|
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-09-14 05:06:09 +04:00
|
|
|
usb_host_claim_interfaces(s, config);
|
2011-08-24 16:45:07 +04:00
|
|
|
usb_linux_update_endp_table(s);
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
|
|
|
|
USBPacket *p)
|
2008-09-14 05:06:09 +04:00
|
|
|
{
|
|
|
|
struct usbdevfs_setinterface si;
|
2010-11-26 13:41:08 +03:00
|
|
|
int i, ret;
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
|
|
|
|
|
2011-08-29 14:49:46 +04:00
|
|
|
for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
|
2011-08-26 18:27:41 +04:00
|
|
|
if (is_isoc(s, USB_TOKEN_IN, i)) {
|
|
|
|
usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
|
|
|
|
}
|
|
|
|
if (is_isoc(s, USB_TOKEN_OUT, i)) {
|
|
|
|
usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
}
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-08-30 15:21:27 +04:00
|
|
|
if (iface >= USB_MAX_INTERFACES) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
return;
|
2011-08-30 15:21:27 +04:00
|
|
|
}
|
|
|
|
|
2008-09-14 05:06:09 +04:00
|
|
|
si.interface = iface;
|
|
|
|
si.altsetting = alt;
|
|
|
|
ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
|
|
|
|
iface, alt, ret, errno);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = ctrl_error();
|
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-08-30 15:21:27 +04:00
|
|
|
|
|
|
|
s->dev.altsetting[iface] = alt;
|
2008-09-14 05:06:09 +04:00
|
|
|
usb_linux_update_endp_table(s);
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
|
2011-02-02 19:36:29 +03:00
|
|
|
int request, int value, int index, int length, uint8_t *data)
|
2008-09-14 05:06:09 +04:00
|
|
|
{
|
2011-02-02 19:36:29 +03:00
|
|
|
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
2008-09-14 05:06:09 +04:00
|
|
|
struct usbdevfs_urb *urb;
|
|
|
|
AsyncURB *aurb;
|
2011-02-02 19:36:29 +03:00
|
|
|
int ret;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
/*
|
2008-09-14 05:06:09 +04:00
|
|
|
* Process certain standard device requests.
|
|
|
|
* These are infrequent and are processed synchronously.
|
|
|
|
*/
|
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
/* Note request is (bRequestType << 8) | bRequest */
|
2012-03-23 15:35:55 +04:00
|
|
|
trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
switch (request) {
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
usb_host_set_address(s, value);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
usb_host_set_config(s, value & 0xff, p);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
usb_host_set_interface(s, index, value, p);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
2012-05-08 15:54:45 +04:00
|
|
|
|
|
|
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
|
|
if (value == 0) { /* clear halt */
|
|
|
|
int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index);
|
|
|
|
clear_halt(s, pid, index & 0x0f);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2012-05-08 15:54:45 +04:00
|
|
|
}
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-09-14 05:06:09 +04:00
|
|
|
|
|
|
|
/* The rest are asynchronous */
|
2011-02-02 19:36:29 +03:00
|
|
|
if (length > sizeof(dev->data_buf)) {
|
|
|
|
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
|
|
|
|
length, sizeof(dev->data_buf));
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
return;
|
2009-08-24 22:56:12 +04:00
|
|
|
}
|
|
|
|
|
2011-05-16 11:13:05 +04:00
|
|
|
aurb = async_alloc(s);
|
2008-09-14 05:06:09 +04:00
|
|
|
aurb->packet = p;
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
/*
|
2008-09-14 05:06:09 +04:00
|
|
|
* Setup ctrl transfer.
|
|
|
|
*
|
2011-04-13 13:45:33 +04:00
|
|
|
* s->ctrl is laid out such that data buffer immediately follows
|
2008-09-14 05:06:09 +04:00
|
|
|
* 'req' struct which is exactly what usbdevfs expects.
|
2010-04-24 20:26:22 +04:00
|
|
|
*/
|
2008-09-14 05:06:09 +04:00
|
|
|
urb = &aurb->urb;
|
|
|
|
|
|
|
|
urb->type = USBDEVFS_URB_TYPE_CONTROL;
|
2012-01-12 16:23:01 +04:00
|
|
|
urb->endpoint = p->ep->nr;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
2011-02-02 19:36:29 +03:00
|
|
|
urb->buffer = &dev->setup_buf;
|
|
|
|
urb->buffer_length = length + 8;
|
2008-09-14 05:06:09 +04:00
|
|
|
|
|
|
|
urb->usercontext = s;
|
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
|
|
|
|
urb->buffer_length, aurb->more);
|
2008-09-14 05:06:09 +04:00
|
|
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
|
|
|
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
|
2008-09-14 05:06:09 +04:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: submit failed. errno %d\n", errno);
|
2008-09-14 05:06:09 +04:00
|
|
|
async_free(aurb);
|
|
|
|
|
|
|
|
switch(errno) {
|
|
|
|
case ETIMEDOUT:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
break;
|
2008-09-14 05:06:09 +04:00
|
|
|
case EPIPE:
|
|
|
|
default:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
break;
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_ASYNC;
|
2008-09-14 05:06:09 +04:00
|
|
|
}
|
|
|
|
|
2012-11-15 12:23:30 +04:00
|
|
|
static void usb_linux_update_endp_table(USBHostDevice *s)
|
2010-11-10 12:06:24 +03:00
|
|
|
{
|
2012-03-29 18:06:28 +04:00
|
|
|
static const char *tname[] = {
|
|
|
|
[USB_ENDPOINT_XFER_CONTROL] = "control",
|
|
|
|
[USB_ENDPOINT_XFER_ISOC] = "isoc",
|
|
|
|
[USB_ENDPOINT_XFER_BULK] = "bulk",
|
|
|
|
[USB_ENDPOINT_XFER_INT] = "int",
|
|
|
|
};
|
|
|
|
uint8_t devep, type;
|
|
|
|
uint16_t mps, v, p;
|
|
|
|
int ep, pid;
|
|
|
|
unsigned int i, configuration = -1, interface = -1, altsetting = -1;
|
2011-08-26 18:27:41 +04:00
|
|
|
struct endp_data *epd;
|
2012-03-29 18:06:28 +04:00
|
|
|
USBDescriptor *d;
|
|
|
|
bool active = false;
|
2010-11-10 12:06:24 +03:00
|
|
|
|
2012-07-03 12:11:21 +04:00
|
|
|
usb_ep_reset(&s->dev);
|
2010-11-26 16:56:17 +03:00
|
|
|
|
2012-03-29 18:06:28 +04:00
|
|
|
for (i = 0;; i += d->bLength) {
|
|
|
|
if (i+2 >= s->descr_len) {
|
|
|
|
break;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
2012-03-29 18:06:28 +04:00
|
|
|
d = (void *)(s->descr + i);
|
|
|
|
if (d->bLength < 2) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"descriptor too short");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
2012-03-29 18:06:28 +04:00
|
|
|
if (i + d->bLength > s->descr_len) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"descriptor too long");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2012-03-29 18:06:28 +04:00
|
|
|
switch (d->bDescriptorType) {
|
|
|
|
case 0:
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"invalid descriptor type");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2012-03-29 18:06:28 +04:00
|
|
|
case USB_DT_DEVICE:
|
|
|
|
if (d->bLength < 0x12) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"device descriptor too short");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2012-03-29 18:06:28 +04:00
|
|
|
}
|
|
|
|
v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
|
|
|
|
p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
|
|
|
|
trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
|
2007-10-05 02:47:34 +04:00
|
|
|
break;
|
2012-03-29 18:06:28 +04:00
|
|
|
case USB_DT_CONFIG:
|
|
|
|
if (d->bLength < 0x09) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"config descriptor too short");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2012-03-29 18:06:28 +04:00
|
|
|
configuration = d->u.config.bConfigurationValue;
|
|
|
|
active = (configuration == s->dev.configuration);
|
|
|
|
trace_usb_host_parse_config(s->bus_num, s->addr,
|
|
|
|
configuration, active);
|
|
|
|
break;
|
|
|
|
case USB_DT_INTERFACE:
|
|
|
|
if (d->bLength < 0x09) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"interface descriptor too short");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2012-03-29 18:06:28 +04:00
|
|
|
}
|
|
|
|
interface = d->u.interface.bInterfaceNumber;
|
|
|
|
altsetting = d->u.interface.bAlternateSetting;
|
|
|
|
active = (configuration == s->dev.configuration) &&
|
|
|
|
(altsetting == s->dev.altsetting[interface]);
|
|
|
|
trace_usb_host_parse_interface(s->bus_num, s->addr,
|
|
|
|
interface, altsetting, active);
|
|
|
|
break;
|
|
|
|
case USB_DT_ENDPOINT:
|
|
|
|
if (d->bLength < 0x07) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"endpoint descriptor too short");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2012-03-29 18:06:28 +04:00
|
|
|
}
|
|
|
|
devep = d->u.endpoint.bEndpointAddress;
|
2011-08-26 18:27:41 +04:00
|
|
|
pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
ep = devep & 0xf;
|
|
|
|
if (ep == 0) {
|
2012-03-29 18:06:28 +04:00
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"invalid endpoint address");
|
2012-11-15 12:23:30 +04:00
|
|
|
return;
|
2011-05-31 13:35:22 +04:00
|
|
|
}
|
|
|
|
|
2012-03-29 18:06:28 +04:00
|
|
|
type = d->u.endpoint.bmAttributes & 0x3;
|
|
|
|
mps = d->u.endpoint.wMaxPacketSize_lo |
|
|
|
|
(d->u.endpoint.wMaxPacketSize_hi << 8);
|
|
|
|
trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
|
|
|
|
(devep & USB_DIR_IN) ? "in" : "out",
|
|
|
|
tname[type], active);
|
|
|
|
|
|
|
|
if (active) {
|
|
|
|
usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
|
|
|
|
assert(usb_ep_get_type(&s->dev, pid, ep) ==
|
|
|
|
USB_ENDPOINT_XFER_INVALID);
|
|
|
|
usb_ep_set_type(&s->dev, pid, ep, type);
|
|
|
|
usb_ep_set_ifnum(&s->dev, pid, ep, interface);
|
|
|
|
if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
|
2012-10-24 20:14:09 +04:00
|
|
|
(type == USB_ENDPOINT_XFER_BULK) &&
|
|
|
|
(pid == USB_TOKEN_OUT)) {
|
2012-03-29 18:06:28 +04:00
|
|
|
usb_ep_set_pipeline(&s->dev, pid, ep, true);
|
|
|
|
}
|
2011-08-29 14:49:46 +04:00
|
|
|
|
2012-03-29 18:06:28 +04:00
|
|
|
epd = get_endp(s, pid, ep);
|
|
|
|
epd->halted = 0;
|
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2012-03-29 18:06:28 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
trace_usb_host_parse_unknown(s->bus_num, s->addr,
|
|
|
|
d->bLength, d->bDescriptorType);
|
|
|
|
break;
|
2007-10-05 02:47:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-30 13:40:45 +04:00
|
|
|
/*
|
|
|
|
* Check if we can safely redirect a usb2 device to a usb1 virtual controller,
|
|
|
|
* this function assumes this is safe, if:
|
|
|
|
* 1) There are no isoc endpoints
|
|
|
|
* 2) There are no interrupt endpoints with a max_packet_size > 64
|
|
|
|
* Note bulk endpoints with a max_packet_size > 64 in theory also are not
|
|
|
|
* usb1 compatible, but in practice this seems to work fine.
|
|
|
|
*/
|
|
|
|
static int usb_linux_full_speed_compat(USBHostDevice *dev)
|
|
|
|
{
|
|
|
|
int i, packet_size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* usb_linux_update_endp_table only registers info about ep in the current
|
|
|
|
* interface altsettings, so we need to parse the descriptors again.
|
|
|
|
*/
|
|
|
|
for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
|
|
|
|
if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
|
|
|
|
switch (dev->descr[i + 3] & 0x3) {
|
|
|
|
case 0x00: /* CONTROL */
|
|
|
|
break;
|
|
|
|
case 0x01: /* ISO */
|
|
|
|
return 0;
|
|
|
|
case 0x02: /* BULK */
|
|
|
|
break;
|
|
|
|
case 0x03: /* INTERRUPT */
|
|
|
|
packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
|
|
|
|
if (packet_size > 64)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
static int usb_host_open(USBHostDevice *dev, int bus_num,
|
2011-08-18 01:35:45 +04:00
|
|
|
int addr, const char *port,
|
|
|
|
const char *prod_name, int speed)
|
2005-11-05 17:22:28 +03:00
|
|
|
{
|
2007-10-05 02:47:34 +04:00
|
|
|
int fd = -1, ret;
|
2008-08-21 23:27:48 +04:00
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_open_started(bus_num, addr);
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (dev->fd != -1) {
|
2009-10-26 17:56:50 +03:00
|
|
|
goto fail;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2011-12-16 14:54:11 +04:00
|
|
|
fd = usb_host_open_device(bus_num, addr);
|
2005-11-05 17:22:28 +03:00
|
|
|
if (fd < 0) {
|
2008-08-21 23:27:48 +04:00
|
|
|
goto fail;
|
2005-11-05 17:22:28 +03:00
|
|
|
}
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: opened %s\n", buf);
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2009-08-31 16:23:59 +04:00
|
|
|
dev->bus_num = bus_num;
|
|
|
|
dev->addr = addr;
|
2011-05-10 13:43:57 +04:00
|
|
|
strcpy(dev->port, port);
|
2009-09-25 18:55:28 +04:00
|
|
|
dev->fd = fd;
|
2009-08-31 16:23:59 +04:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
/* read the device description */
|
|
|
|
dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
|
|
|
|
if (dev->descr_len <= 0) {
|
2008-08-21 23:31:10 +04:00
|
|
|
perror("husb: reading device data failed");
|
2005-11-05 17:22:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
#ifdef DEBUG
|
2005-11-14 00:53:15 +03:00
|
|
|
{
|
2007-10-05 02:47:34 +04:00
|
|
|
int x;
|
|
|
|
printf("=== begin dumping device descriptor data ===\n");
|
2010-04-24 20:26:22 +04:00
|
|
|
for (x = 0; x < dev->descr_len; x++) {
|
2007-10-05 02:47:34 +04:00
|
|
|
printf("%02x ", dev->descr[x]);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2007-10-05 02:47:34 +04:00
|
|
|
printf("\n=== end dumping device descriptor data ===\n");
|
2005-11-05 17:22:28 +03:00
|
|
|
}
|
2005-11-06 19:13:29 +03:00
|
|
|
#endif
|
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-08-24 16:45:07 +04:00
|
|
|
/* start unconfigured -- we'll wait for the guest to set a configuration */
|
|
|
|
if (!usb_host_claim_interfaces(dev, 0)) {
|
2007-10-05 02:47:34 +04:00
|
|
|
goto fail;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2012-07-03 12:11:21 +04:00
|
|
|
usb_ep_init(&dev->dev);
|
2012-11-15 12:23:30 +04:00
|
|
|
usb_linux_update_endp_table(dev);
|
2007-10-05 02:47:34 +04:00
|
|
|
|
2011-05-31 13:35:18 +04:00
|
|
|
if (speed == -1) {
|
|
|
|
struct usbdevfs_connectinfo ci;
|
|
|
|
|
|
|
|
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ci.slow) {
|
|
|
|
speed = USB_SPEED_LOW;
|
|
|
|
} else {
|
|
|
|
speed = USB_SPEED_HIGH;
|
|
|
|
}
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-05-31 13:35:18 +04:00
|
|
|
dev->dev.speed = speed;
|
2011-05-27 16:27:18 +04:00
|
|
|
dev->dev.speedmask = (1 << speed);
|
2011-05-30 13:40:45 +04:00
|
|
|
if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
|
|
|
|
dev->dev.speedmask |= USB_SPEED_MASK_FULL;
|
|
|
|
}
|
2011-05-31 13:35:18 +04:00
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_open_success(bus_num, addr);
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (!prod_name || prod_name[0] == '\0') {
|
2009-12-09 19:07:51 +03:00
|
|
|
snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
|
2008-08-21 23:28:55 +04:00
|
|
|
"host:%d.%d", bus_num, addr);
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2009-12-09 19:07:51 +03:00
|
|
|
pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
|
2008-08-21 23:28:55 +04:00
|
|
|
prod_name);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2006-06-27 01:00:51 +04:00
|
|
|
|
2011-05-27 21:05:15 +04:00
|
|
|
ret = usb_device_attach(&dev->dev);
|
|
|
|
if (ret) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2008-08-21 23:31:10 +04:00
|
|
|
/* USB devio uses 'write' flag to check for async completions */
|
|
|
|
qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
|
2008-08-21 23:27:48 +04:00
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
return 0;
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2007-10-05 02:47:34 +04:00
|
|
|
fail:
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_open_failure(bus_num, addr);
|
2011-06-06 11:45:20 +04:00
|
|
|
if (dev->fd != -1) {
|
|
|
|
close(dev->fd);
|
|
|
|
dev->fd = -1;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_close(USBHostDevice *dev)
|
|
|
|
{
|
2010-11-26 13:41:08 +03:00
|
|
|
int i;
|
|
|
|
|
2011-10-28 18:13:50 +04:00
|
|
|
if (dev->fd == -1) {
|
2009-10-26 17:56:50 +03:00
|
|
|
return -1;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_close(dev->bus_num, dev->addr);
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
|
|
|
|
dev->closing = 1;
|
2011-08-29 14:49:46 +04:00
|
|
|
for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
|
2011-08-26 18:27:41 +04:00
|
|
|
if (is_isoc(dev, USB_TOKEN_IN, i)) {
|
|
|
|
usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
|
|
|
|
}
|
|
|
|
if (is_isoc(dev, USB_TOKEN_OUT, i)) {
|
|
|
|
usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
|
2010-11-26 13:41:08 +03:00
|
|
|
}
|
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
async_complete(dev);
|
|
|
|
dev->closing = 0;
|
2011-10-28 18:13:50 +04:00
|
|
|
if (dev->dev.attached) {
|
|
|
|
usb_device_detach(&dev->dev);
|
|
|
|
}
|
2011-11-16 15:37:17 +04:00
|
|
|
usb_host_do_reset(dev);
|
2009-10-26 17:56:50 +03:00
|
|
|
close(dev->fd);
|
|
|
|
dev->fd = -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-20 16:06:26 +04:00
|
|
|
static void usb_host_exit_notifier(struct Notifier *n, void *data)
|
2010-06-16 16:16:11 +04:00
|
|
|
{
|
|
|
|
USBHostDevice *s = container_of(n, USBHostDevice, exit);
|
|
|
|
|
2012-01-05 18:49:18 +04:00
|
|
|
usb_host_release_port(s);
|
2010-06-16 16:16:11 +04:00
|
|
|
if (s->fd != -1) {
|
2011-11-16 15:37:17 +04:00
|
|
|
usb_host_do_reset(s);;
|
2010-06-16 16:16:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-08 15:02:16 +04:00
|
|
|
/*
|
|
|
|
* This is *NOT* about restoring state. We have absolutely no idea
|
|
|
|
* what state the host device is in at the moment and whenever it is
|
|
|
|
* still present in the first place. Attemping to contine where we
|
|
|
|
* left off is impossible.
|
|
|
|
*
|
|
|
|
* What we are going to to to here is emulate a surprise removal of
|
|
|
|
* the usb device passed through, then kick host scan so the device
|
|
|
|
* will get re-attached (and re-initialized by the guest) in case it
|
|
|
|
* is still present.
|
|
|
|
*
|
|
|
|
* As the device removal will change the state of other devices (usb
|
|
|
|
* host controller, most likely interrupt controller too) we have to
|
|
|
|
* wait with it until *all* vmstate is loaded. Thus post_load just
|
|
|
|
* kicks a bottom half which then does the actual work.
|
|
|
|
*/
|
|
|
|
static void usb_host_post_load_bh(void *opaque)
|
|
|
|
{
|
|
|
|
USBHostDevice *dev = opaque;
|
|
|
|
|
|
|
|
if (dev->fd != -1) {
|
|
|
|
usb_host_close(dev);
|
|
|
|
}
|
|
|
|
if (dev->dev.attached) {
|
|
|
|
usb_device_detach(&dev->dev);
|
|
|
|
}
|
|
|
|
usb_host_auto_check(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_post_load(void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
USBHostDevice *dev = opaque;
|
|
|
|
|
|
|
|
qemu_bh_schedule(dev->bh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
static int usb_host_initfn(USBDevice *dev)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
|
|
|
|
2012-11-17 15:47:15 +04:00
|
|
|
dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
2009-10-26 17:56:50 +03:00
|
|
|
dev->auto_attach = 0;
|
|
|
|
s->fd = -1;
|
2011-08-24 15:34:17 +04:00
|
|
|
s->hub_fd = -1;
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
QTAILQ_INSERT_TAIL(&hostdevs, s, next);
|
2010-06-16 16:16:11 +04:00
|
|
|
s->exit.notify = usb_host_exit_notifier;
|
|
|
|
qemu_add_exit_notifier(&s->exit);
|
2012-06-08 15:02:16 +04:00
|
|
|
s->bh = qemu_bh_new(usb_host_post_load_bh, s);
|
2009-10-26 17:56:50 +03:00
|
|
|
usb_host_auto_check(NULL);
|
2011-08-24 15:34:17 +04:00
|
|
|
|
|
|
|
if (s->match.bus_num != 0 && s->match.port != NULL) {
|
2011-09-13 13:37:47 +04:00
|
|
|
usb_host_claim_port(s);
|
2011-08-24 15:34:17 +04:00
|
|
|
}
|
2012-03-22 13:48:03 +04:00
|
|
|
add_boot_device_path(s->bootindex, &dev->qdev, NULL);
|
2009-10-26 17:56:50 +03:00
|
|
|
return 0;
|
2005-11-06 19:13:29 +03:00
|
|
|
}
|
2005-11-05 17:22:28 +03:00
|
|
|
|
2011-08-31 13:44:24 +04:00
|
|
|
static const VMStateDescription vmstate_usb_host = {
|
|
|
|
.name = "usb-host",
|
2012-06-08 15:02:16 +04:00
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.post_load = usb_host_post_load,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_USB_DEVICE(dev, USBHostDevice),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
2011-08-31 13:44:24 +04:00
|
|
|
};
|
|
|
|
|
2011-12-08 07:34:16 +04:00
|
|
|
static Property usb_host_dev_properties[] = {
|
|
|
|
DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0),
|
|
|
|
DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0),
|
|
|
|
DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
|
|
|
|
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
|
|
|
|
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
|
|
|
|
DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4),
|
2012-03-22 13:48:03 +04:00
|
|
|
DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1),
|
2012-03-22 18:28:45 +04:00
|
|
|
DEFINE_PROP_BIT("pipeline", USBHostDevice, options,
|
|
|
|
USB_HOST_OPT_PIPELINE, true),
|
2011-12-08 07:34:16 +04:00
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
2011-12-16 00:53:10 +04:00
|
|
|
static void usb_host_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-16 00:53:10 +04:00
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
|
|
|
|
|
|
uc->init = usb_host_initfn;
|
|
|
|
uc->product_desc = "USB Host Device";
|
|
|
|
uc->cancel_packet = usb_host_async_cancel;
|
|
|
|
uc->handle_data = usb_host_handle_data;
|
|
|
|
uc->handle_control = usb_host_handle_control;
|
|
|
|
uc->handle_reset = usb_host_handle_reset;
|
|
|
|
uc->handle_destroy = usb_host_handle_destroy;
|
2011-12-08 07:34:16 +04:00
|
|
|
dc->vmsd = &vmstate_usb_host;
|
|
|
|
dc->props = usb_host_dev_properties;
|
2011-12-16 00:53:10 +04:00
|
|
|
}
|
|
|
|
|
2011-12-08 07:34:16 +04:00
|
|
|
static TypeInfo usb_host_dev_info = {
|
|
|
|
.name = "usb-host",
|
|
|
|
.parent = TYPE_USB_DEVICE,
|
|
|
|
.instance_size = sizeof(USBHostDevice),
|
|
|
|
.class_init = usb_host_class_initfn,
|
2009-08-31 16:23:59 +04:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void usb_host_register_types(void)
|
2009-08-31 16:23:59 +04:00
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
type_register_static(&usb_host_dev_info);
|
2011-12-09 00:56:53 +04:00
|
|
|
usb_legacy_register("usb-host", "host", usb_host_device_open);
|
2009-08-31 16:23:59 +04:00
|
|
|
}
|
2012-02-09 18:20:55 +04:00
|
|
|
|
|
|
|
type_init(usb_host_register_types)
|
2009-08-31 16:23:59 +04:00
|
|
|
|
2012-02-27 18:18:47 +04:00
|
|
|
USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
|
2008-08-21 23:28:55 +04:00
|
|
|
{
|
2009-11-27 15:05:53 +03:00
|
|
|
struct USBAutoFilter filter;
|
2009-10-26 17:56:50 +03:00
|
|
|
USBDevice *dev;
|
|
|
|
char *p;
|
|
|
|
|
2012-02-27 18:18:47 +04:00
|
|
|
dev = usb_create(bus, "usb-host");
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2008-09-14 05:07:41 +04:00
|
|
|
if (strstr(devname, "auto:")) {
|
2010-04-24 20:26:22 +04:00
|
|
|
if (parse_filter(devname, &filter) < 0) {
|
2009-10-26 17:56:50 +03:00
|
|
|
goto fail;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
} else {
|
|
|
|
if ((p = strchr(devname, '.'))) {
|
2009-11-27 15:05:53 +03:00
|
|
|
filter.bus_num = strtoul(devname, NULL, 0);
|
|
|
|
filter.addr = strtoul(p + 1, NULL, 0);
|
|
|
|
filter.vendor_id = 0;
|
|
|
|
filter.product_id = 0;
|
2009-10-26 17:56:50 +03:00
|
|
|
} else if ((p = strchr(devname, ':'))) {
|
2009-11-27 15:05:53 +03:00
|
|
|
filter.bus_num = 0;
|
|
|
|
filter.addr = 0;
|
2009-10-26 17:56:50 +03:00
|
|
|
filter.vendor_id = strtoul(devname, NULL, 16);
|
2009-11-27 15:05:53 +03:00
|
|
|
filter.product_id = strtoul(p + 1, NULL, 16);
|
2009-10-26 17:56:50 +03:00
|
|
|
} else {
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
}
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2009-11-27 15:05:53 +03:00
|
|
|
qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
|
|
|
|
qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
|
2009-10-26 17:56:50 +03:00
|
|
|
qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
|
|
|
|
qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
|
2010-01-15 14:56:41 +03:00
|
|
|
qdev_init_nofail(&dev->qdev);
|
2009-10-26 17:56:50 +03:00
|
|
|
return dev;
|
2008-09-14 05:07:41 +04:00
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
fail:
|
|
|
|
qdev_free(&dev->qdev);
|
|
|
|
return NULL;
|
2008-08-21 23:28:55 +04:00
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
|
|
|
|
int usb_host_device_close(const char *devname)
|
|
|
|
{
|
2009-10-26 17:56:50 +03:00
|
|
|
#if 0
|
2008-09-14 05:07:41 +04:00
|
|
|
char product_name[PRODUCT_NAME_SZ];
|
|
|
|
int bus_num, addr;
|
|
|
|
USBHostDevice *s;
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (strstr(devname, "auto:")) {
|
2008-09-14 05:07:41 +04:00
|
|
|
return usb_host_auto_del(devname);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (usb_host_find_device(&bus_num, &addr, product_name,
|
|
|
|
sizeof(product_name), devname) < 0) {
|
2008-09-14 05:07:41 +04:00
|
|
|
return -1;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
s = hostdev_find(bus_num, addr);
|
|
|
|
if (s) {
|
2009-08-31 16:24:00 +04:00
|
|
|
usb_device_delete_addr(s->bus_num, s->dev.addr);
|
2008-09-14 05:07:41 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
#endif
|
2008-09-14 05:07:41 +04:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2009-08-31 16:24:00 +04:00
|
|
|
|
2008-10-08 00:06:37 +04:00
|
|
|
/*
|
|
|
|
* Read sys file-system device file
|
|
|
|
*
|
|
|
|
* @line address of buffer to put file contents in
|
|
|
|
* @line_size size of line
|
|
|
|
* @device_file path to device file (printf format string)
|
|
|
|
* @device_name device being opened (inserted into device_file)
|
|
|
|
*
|
|
|
|
* @return 0 failed, 1 succeeded ('line' contains data)
|
|
|
|
*/
|
2010-04-24 20:26:22 +04:00
|
|
|
static int usb_host_read_file(char *line, size_t line_size,
|
|
|
|
const char *device_file, const char *device_name)
|
2008-10-08 00:06:37 +04:00
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
int ret = 0;
|
|
|
|
char filename[PATH_MAX];
|
|
|
|
|
2011-12-16 14:54:11 +04:00
|
|
|
snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
|
2008-12-28 18:45:20 +03:00
|
|
|
device_file);
|
2008-10-08 00:06:37 +04:00
|
|
|
f = fopen(filename, "r");
|
|
|
|
if (f) {
|
2010-01-20 02:56:17 +03:00
|
|
|
ret = fgets(line, line_size, f) != NULL;
|
2008-10-08 00:06:37 +04:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use /sys/bus/usb/devices/ directory to determine host's USB
|
|
|
|
* devices.
|
|
|
|
*
|
|
|
|
* This code is based on Robert Schiele's original patches posted to
|
|
|
|
* the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
|
|
|
|
*/
|
2011-12-16 14:54:11 +04:00
|
|
|
static int usb_host_scan(void *opaque, USBScanFunc *func)
|
2008-10-08 00:06:37 +04:00
|
|
|
{
|
2009-08-01 01:16:51 +04:00
|
|
|
DIR *dir = NULL;
|
2008-10-08 00:06:37 +04:00
|
|
|
char line[1024];
|
2011-05-10 13:43:57 +04:00
|
|
|
int bus_num, addr, speed, class_id, product_id, vendor_id;
|
2008-10-08 00:06:37 +04:00
|
|
|
int ret = 0;
|
2011-05-10 13:43:57 +04:00
|
|
|
char port[MAX_PORTLEN];
|
2008-10-08 00:06:37 +04:00
|
|
|
char product_name[512];
|
|
|
|
struct dirent *de;
|
|
|
|
|
2011-12-16 14:54:11 +04:00
|
|
|
dir = opendir("/sys/bus/usb/devices");
|
2008-10-08 00:06:37 +04:00
|
|
|
if (!dir) {
|
2011-12-16 14:54:11 +04:00
|
|
|
perror("husb: opendir /sys/bus/usb/devices");
|
|
|
|
fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((de = readdir(dir))) {
|
|
|
|
if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
|
2011-05-10 13:43:57 +04:00
|
|
|
if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
|
|
|
|
continue;
|
2010-11-10 12:06:23 +03:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (sscanf(line, "%d", &addr) != 1) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-12-28 18:45:20 +03:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
|
2010-04-24 20:26:22 +04:00
|
|
|
de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (sscanf(line, "%x", &class_id) != 1) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "idVendor",
|
|
|
|
de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (sscanf(line, "%x", &vendor_id) != 1) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-12-28 18:45:20 +03:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "idProduct",
|
2010-04-24 20:26:22 +04:00
|
|
|
de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (sscanf(line, "%x", &product_id) != 1) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-12-28 18:45:20 +03:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "product",
|
|
|
|
de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
*product_name = 0;
|
|
|
|
} else {
|
2010-04-24 20:26:22 +04:00
|
|
|
if (strlen(line) > 0) {
|
2008-10-08 00:06:37 +04:00
|
|
|
line[strlen(line) - 1] = '\0';
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
pstrcpy(product_name, sizeof(product_name), line);
|
|
|
|
}
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-05-31 13:35:19 +04:00
|
|
|
if (!strcmp(line, "5000\n")) {
|
|
|
|
speed = USB_SPEED_SUPER;
|
|
|
|
} else if (!strcmp(line, "480\n")) {
|
2008-10-08 00:06:37 +04:00
|
|
|
speed = USB_SPEED_HIGH;
|
2010-04-24 20:26:22 +04:00
|
|
|
} else if (!strcmp(line, "1.5\n")) {
|
2008-10-08 00:06:37 +04:00
|
|
|
speed = USB_SPEED_LOW;
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2008-10-08 00:06:37 +04:00
|
|
|
speed = USB_SPEED_FULL;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
|
2011-05-10 13:43:57 +04:00
|
|
|
ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
|
2008-10-08 00:06:37 +04:00
|
|
|
product_id, product_name, speed);
|
2010-04-24 20:26:22 +04:00
|
|
|
if (ret) {
|
2008-10-08 00:06:37 +04:00
|
|
|
goto the_end;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
the_end:
|
2010-04-24 20:26:22 +04:00
|
|
|
if (dir) {
|
2008-10-08 00:06:37 +04:00
|
|
|
closedir(dir);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-10-08 00:06:37 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-08-21 23:28:55 +04:00
|
|
|
static QEMUTimer *usb_auto_timer;
|
2012-11-14 18:51:18 +04:00
|
|
|
static VMChangeStateEntry *usb_vmstate;
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2011-08-18 01:35:45 +04:00
|
|
|
static int usb_host_auto_scan(void *opaque, int bus_num,
|
|
|
|
int addr, const char *port,
|
2009-10-26 17:56:50 +03:00
|
|
|
int class_id, int vendor_id, int product_id,
|
|
|
|
const char *product_name, int speed)
|
2008-08-21 23:28:55 +04:00
|
|
|
{
|
|
|
|
struct USBAutoFilter *f;
|
2009-10-26 17:56:50 +03:00
|
|
|
struct USBHostDevice *s;
|
2008-08-21 23:28:55 +04:00
|
|
|
|
|
|
|
/* Ignore hubs */
|
|
|
|
if (class_id == 9)
|
|
|
|
return 0;
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
|
|
|
f = &s->match;
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (f->bus_num > 0 && f->bus_num != bus_num) {
|
2008-08-21 23:28:55 +04:00
|
|
|
continue;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
if (f->addr > 0 && f->addr != addr) {
|
2008-08-21 23:28:55 +04:00
|
|
|
continue;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-05-10 14:07:42 +04:00
|
|
|
if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
|
|
|
|
continue;
|
|
|
|
}
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
|
2008-08-21 23:28:55 +04:00
|
|
|
continue;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (f->product_id > 0 && f->product_id != product_id) {
|
2008-08-21 23:28:55 +04:00
|
|
|
continue;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-08-21 23:28:55 +04:00
|
|
|
/* We got a match */
|
2011-08-24 15:45:06 +04:00
|
|
|
s->seen++;
|
|
|
|
if (s->errcount >= 3) {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2009-10-07 03:15:57 +04:00
|
|
|
/* Already attached ? */
|
2010-04-24 20:26:22 +04:00
|
|
|
if (s->fd != -1) {
|
2008-08-21 23:28:55 +04:00
|
|
|
return 0;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2010-02-07 02:03:50 +03:00
|
|
|
DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
|
2008-08-21 23:28:55 +04:00
|
|
|
|
2011-08-24 15:45:06 +04:00
|
|
|
if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
|
|
|
|
s->errcount++;
|
|
|
|
}
|
2011-05-31 13:35:24 +04:00
|
|
|
break;
|
2008-08-21 23:28:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-14 18:51:18 +04:00
|
|
|
static void usb_host_vm_state(void *unused, int running, RunState state)
|
|
|
|
{
|
|
|
|
if (running) {
|
|
|
|
usb_host_auto_check(unused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
static void usb_host_auto_check(void *unused)
|
2008-08-21 23:28:55 +04:00
|
|
|
{
|
2009-10-26 17:56:50 +03:00
|
|
|
struct USBHostDevice *s;
|
|
|
|
int unconnected = 0;
|
|
|
|
|
2012-06-08 15:02:52 +04:00
|
|
|
if (runstate_is_running()) {
|
|
|
|
usb_host_scan(NULL, usb_host_auto_scan);
|
2009-10-26 17:56:50 +03:00
|
|
|
|
2012-06-08 15:02:52 +04:00
|
|
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
|
|
|
if (s->fd == -1) {
|
|
|
|
unconnected++;
|
|
|
|
}
|
|
|
|
if (s->seen == 0) {
|
|
|
|
s->errcount = 0;
|
|
|
|
}
|
|
|
|
s->seen = 0;
|
2011-08-24 15:45:06 +04:00
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
|
2012-06-08 15:02:52 +04:00
|
|
|
if (unconnected == 0) {
|
|
|
|
/* nothing to watch */
|
|
|
|
if (usb_auto_timer) {
|
|
|
|
qemu_del_timer(usb_auto_timer);
|
|
|
|
trace_usb_host_auto_scan_disabled();
|
|
|
|
}
|
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
}
|
|
|
|
|
2012-11-14 18:51:18 +04:00
|
|
|
if (!usb_vmstate) {
|
|
|
|
usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
|
|
|
|
}
|
2009-10-26 17:56:50 +03:00
|
|
|
if (!usb_auto_timer) {
|
2011-03-11 18:47:48 +03:00
|
|
|
usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
|
2010-04-24 20:26:22 +04:00
|
|
|
if (!usb_auto_timer) {
|
2009-10-26 17:56:50 +03:00
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2011-08-22 16:13:20 +04:00
|
|
|
trace_usb_host_auto_scan_enabled();
|
2009-10-26 17:56:50 +03:00
|
|
|
}
|
2011-03-11 18:47:48 +03:00
|
|
|
qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
|
2008-08-21 23:28:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-09-14 05:07:41 +04:00
|
|
|
* Autoconnect filter
|
|
|
|
* Format:
|
|
|
|
* auto:bus:dev[:vid:pid]
|
|
|
|
* auto:bus.dev[:vid:pid]
|
|
|
|
*
|
|
|
|
* bus - bus number (dec, * means any)
|
|
|
|
* dev - device number (dec, * means any)
|
|
|
|
* vid - vendor id (hex, * means any)
|
|
|
|
* pid - product id (hex, * means any)
|
|
|
|
*
|
|
|
|
* See 'lsusb' output.
|
2008-08-21 23:28:55 +04:00
|
|
|
*/
|
2008-09-14 05:07:41 +04:00
|
|
|
static int parse_filter(const char *spec, struct USBAutoFilter *f)
|
2008-08-21 23:28:55 +04:00
|
|
|
{
|
2008-09-14 05:07:41 +04:00
|
|
|
enum { BUS, DEV, VID, PID, DONE };
|
|
|
|
const char *p = spec;
|
|
|
|
int i;
|
|
|
|
|
2009-11-27 15:05:53 +03:00
|
|
|
f->bus_num = 0;
|
|
|
|
f->addr = 0;
|
|
|
|
f->vendor_id = 0;
|
|
|
|
f->product_id = 0;
|
2008-09-14 05:07:41 +04:00
|
|
|
|
|
|
|
for (i = BUS; i < DONE; i++) {
|
2010-04-24 20:26:22 +04:00
|
|
|
p = strpbrk(p, ":.");
|
|
|
|
if (!p) {
|
|
|
|
break;
|
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
p++;
|
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (*p == '*') {
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
switch(i) {
|
|
|
|
case BUS: f->bus_num = strtol(p, NULL, 10); break;
|
|
|
|
case DEV: f->addr = strtol(p, NULL, 10); break;
|
|
|
|
case VID: f->vendor_id = strtol(p, NULL, 16); break;
|
|
|
|
case PID: f->product_id = strtol(p, NULL, 16); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < DEV) {
|
|
|
|
fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-06 19:13:29 +03:00
|
|
|
/**********************/
|
|
|
|
/* USB host device info */
|
|
|
|
|
|
|
|
struct usb_class_info {
|
|
|
|
int class;
|
|
|
|
const char *class_name;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct usb_class_info usb_class_info[] = {
|
|
|
|
{ USB_CLASS_AUDIO, "Audio"},
|
|
|
|
{ USB_CLASS_COMM, "Communication"},
|
|
|
|
{ USB_CLASS_HID, "HID"},
|
|
|
|
{ USB_CLASS_HUB, "Hub" },
|
|
|
|
{ USB_CLASS_PHYSICAL, "Physical" },
|
|
|
|
{ USB_CLASS_PRINTER, "Printer" },
|
|
|
|
{ USB_CLASS_MASS_STORAGE, "Storage" },
|
|
|
|
{ USB_CLASS_CDC_DATA, "Data" },
|
|
|
|
{ USB_CLASS_APP_SPEC, "Application Specific" },
|
|
|
|
{ USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
|
|
|
|
{ USB_CLASS_STILL_IMAGE, "Still Image" },
|
2007-10-05 02:47:34 +04:00
|
|
|
{ USB_CLASS_CSCID, "Smart Card" },
|
2005-11-06 19:13:29 +03:00
|
|
|
{ USB_CLASS_CONTENT_SEC, "Content Security" },
|
|
|
|
{ -1, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *usb_class_str(uint8_t class)
|
2005-11-05 17:22:28 +03:00
|
|
|
{
|
2005-11-06 19:13:29 +03:00
|
|
|
const struct usb_class_info *p;
|
|
|
|
for(p = usb_class_info; p->class != -1; p++) {
|
2010-04-24 20:26:22 +04:00
|
|
|
if (p->class == class) {
|
2005-11-06 19:13:29 +03:00
|
|
|
break;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2005-11-05 17:22:28 +03:00
|
|
|
}
|
2005-11-06 19:13:29 +03:00
|
|
|
return p->class_name;
|
|
|
|
}
|
|
|
|
|
2011-08-18 01:35:45 +04:00
|
|
|
static void usb_info_device(Monitor *mon, int bus_num,
|
|
|
|
int addr, const char *port,
|
2011-05-10 13:43:57 +04:00
|
|
|
int class_id, int vendor_id, int product_id,
|
2007-11-18 04:44:38 +03:00
|
|
|
const char *product_name,
|
|
|
|
int speed)
|
2005-11-06 19:13:29 +03:00
|
|
|
{
|
|
|
|
const char *class_str, *speed_str;
|
|
|
|
|
|
|
|
switch(speed) {
|
2007-09-17 01:08:06 +04:00
|
|
|
case USB_SPEED_LOW:
|
|
|
|
speed_str = "1.5";
|
2005-11-06 19:13:29 +03:00
|
|
|
break;
|
2007-09-17 01:08:06 +04:00
|
|
|
case USB_SPEED_FULL:
|
|
|
|
speed_str = "12";
|
2005-11-06 19:13:29 +03:00
|
|
|
break;
|
2007-09-17 01:08:06 +04:00
|
|
|
case USB_SPEED_HIGH:
|
|
|
|
speed_str = "480";
|
2005-11-06 19:13:29 +03:00
|
|
|
break;
|
2011-05-31 13:35:19 +04:00
|
|
|
case USB_SPEED_SUPER:
|
|
|
|
speed_str = "5000";
|
|
|
|
break;
|
2005-11-06 19:13:29 +03:00
|
|
|
default:
|
2007-09-17 01:08:06 +04:00
|
|
|
speed_str = "?";
|
2005-11-06 19:13:29 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-05-10 13:43:57 +04:00
|
|
|
monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
|
|
|
|
bus_num, addr, port, speed_str);
|
2005-11-06 19:13:29 +03:00
|
|
|
class_str = usb_class_str(class_id);
|
2010-04-24 20:26:22 +04:00
|
|
|
if (class_str) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " %s:", class_str);
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " Class %02x:", class_id);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
|
2010-04-24 20:26:22 +04:00
|
|
|
if (product_name[0] != '\0') {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, ", %s", product_name);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\n");
|
2005-11-06 19:13:29 +03:00
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
static int usb_host_info_device(void *opaque, int bus_num, int addr,
|
2011-08-18 01:35:45 +04:00
|
|
|
const char *path, int class_id,
|
2007-09-17 01:08:06 +04:00
|
|
|
int vendor_id, int product_id,
|
2005-11-06 19:13:29 +03:00
|
|
|
const char *product_name,
|
|
|
|
int speed)
|
|
|
|
{
|
2009-09-07 23:00:18 +04:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2011-05-10 13:43:57 +04:00
|
|
|
usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
|
2005-11-06 19:13:29 +03:00
|
|
|
product_name, speed);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-22 19:04:31 +04:00
|
|
|
static void dec2str(int val, char *str, size_t size)
|
2008-09-14 05:07:41 +04:00
|
|
|
{
|
2010-04-24 20:26:22 +04:00
|
|
|
if (val == 0) {
|
2008-09-22 19:04:31 +04:00
|
|
|
snprintf(str, size, "*");
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
|
|
|
snprintf(str, size, "%d", val);
|
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
}
|
|
|
|
|
2008-09-22 19:04:31 +04:00
|
|
|
static void hex2str(int val, char *str, size_t size)
|
2008-09-14 05:07:41 +04:00
|
|
|
{
|
2010-04-24 20:26:22 +04:00
|
|
|
if (val == 0) {
|
2008-09-22 19:04:31 +04:00
|
|
|
snprintf(str, size, "*");
|
2010-04-24 20:26:22 +04:00
|
|
|
} else {
|
2009-10-26 17:56:50 +03:00
|
|
|
snprintf(str, size, "%04x", val);
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
2008-09-14 05:07:41 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void usb_host_info(Monitor *mon)
|
2005-11-06 19:13:29 +03:00
|
|
|
{
|
2008-09-14 05:07:41 +04:00
|
|
|
struct USBAutoFilter *f;
|
2009-10-26 17:56:50 +03:00
|
|
|
struct USBHostDevice *s;
|
2008-09-14 05:07:41 +04:00
|
|
|
|
2009-09-07 23:00:18 +04:00
|
|
|
usb_host_scan(mon, usb_host_info_device);
|
2008-09-14 05:07:41 +04:00
|
|
|
|
2010-04-24 20:26:22 +04:00
|
|
|
if (QTAILQ_EMPTY(&hostdevs)) {
|
2009-10-26 17:56:50 +03:00
|
|
|
return;
|
2010-04-24 20:26:22 +04:00
|
|
|
}
|
|
|
|
|
2009-10-26 17:56:50 +03:00
|
|
|
monitor_printf(mon, " Auto filters:\n");
|
|
|
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
2008-09-14 05:07:41 +04:00
|
|
|
char bus[10], addr[10], vid[10], pid[10];
|
2009-10-26 17:56:50 +03:00
|
|
|
f = &s->match;
|
2008-09-22 19:04:31 +04:00
|
|
|
dec2str(f->bus_num, bus, sizeof(bus));
|
|
|
|
dec2str(f->addr, addr, sizeof(addr));
|
|
|
|
hex2str(f->vendor_id, vid, sizeof(vid));
|
|
|
|
hex2str(f->product_id, pid, sizeof(pid));
|
2011-05-10 14:07:42 +04:00
|
|
|
monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n",
|
|
|
|
bus, addr, f->port ? f->port : "*", vid, pid);
|
2008-09-14 05:07:41 +04:00
|
|
|
}
|
2005-11-05 17:22:28 +03:00
|
|
|
}
|