Merge remote-tracking branch 'kraxel/usb.37' into staging

* kraxel/usb.37:
  usb-redir: Improve some debugging messages
  usb-redir: Try to keep our buffer size near the target size
  usb-redir: Pre-fill our isoc input buffer before sending pkts to the host
  usb-redir: Dynamically adjust iso buffering size based on ep interval
  usb-redir: Clear iso / irq error when stopping the stream
  usb: link packets to endpoints not devices
  usb: add max_packet_size to USBEndpoint
  usb/debug: add usb_ep_dump
  usb-desc: USBEndpoint support
  usb: add ifnum to USBEndpoint
  usb: add USBEndpoint
  xhci: Initial xHCI implementation
  usb: add audio device model
  usb-desc: audio endpoint support
  usb: track altsetting in USBDevice
  usb: track configuration and interface count in USBDevice.
  usb-host: rip out legacy procfs support
master
Anthony Liguori 2012-01-19 08:34:38 -06:00
commit 9ca2140ab1
25 changed files with 3973 additions and 471 deletions

View File

@ -102,7 +102,7 @@ common-obj-y += scsi-disk.o cdrom.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += hid.o
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o usb-audio.o
common-obj-$(CONFIG_SSI) += ssi.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o
@ -211,6 +211,7 @@ hw-obj-$(CONFIG_PCKBD) += pckbd.o
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
hw-obj-$(CONFIG_USB_XHCI) += usb-xhci.o
hw-obj-$(CONFIG_FDC) += fdc.o
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o

View File

@ -4,6 +4,7 @@ CONFIG_VIRTIO=y
CONFIG_USB_UHCI=y
CONFIG_USB_OHCI=y
CONFIG_USB_EHCI=y
CONFIG_USB_XHCI=y
CONFIG_NE2000_PCI=y
CONFIG_EEPRO100_PCI=y
CONFIG_PCNET_PCI=y

View File

@ -120,3 +120,6 @@
#define PCI_VENDOR_ID_XEN 0x5853
#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001
#define PCI_VENDOR_ID_NEC 0x1033
#define PCI_DEVICE_ID_NEC_UPD720200 0x0194

704
hw/usb-audio.c Normal file
View File

@ -0,0 +1,704 @@
/*
* QEMU USB audio device
*
* written by:
* H. Peter Anvin <hpa@linux.intel.com>
* Gerd Hoffmann <kraxel@redhat.com>
*
* lousely based on usb net device code which is:
*
* Copyright (c) 2006 Thomas Sailer
* Copyright (c) 2008 Andrzej Zaborowski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu-common.h"
#include "usb.h"
#include "usb-desc.h"
#include "hw.h"
#include "audiodev.h"
#include "audio/audio.h"
#define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */
#define USBAUDIO_PRODUCT_NUM 0x0002
#define DEV_CONFIG_VALUE 1 /* The one and only */
/* Descriptor subtypes for AC interfaces */
#define DST_AC_HEADER 1
#define DST_AC_INPUT_TERMINAL 2
#define DST_AC_OUTPUT_TERMINAL 3
#define DST_AC_FEATURE_UNIT 6
/* Descriptor subtypes for AS interfaces */
#define DST_AS_GENERAL 1
#define DST_AS_FORMAT_TYPE 2
/* Descriptor subtypes for endpoints */
#define DST_EP_GENERAL 1
enum usb_audio_strings {
STRING_NULL,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_SERIALNUMBER,
STRING_CONFIG,
STRING_USBAUDIO_CONTROL,
STRING_INPUT_TERMINAL,
STRING_FEATURE_UNIT,
STRING_OUTPUT_TERMINAL,
STRING_NULL_STREAM,
STRING_REAL_STREAM,
};
static const USBDescStrings usb_audio_stringtable = {
[STRING_MANUFACTURER] = "QEMU",
[STRING_PRODUCT] = "QEMU USB Audio",
[STRING_SERIALNUMBER] = "1",
[STRING_CONFIG] = "Audio Configuration",
[STRING_USBAUDIO_CONTROL] = "Audio Device",
[STRING_INPUT_TERMINAL] = "Audio Output Pipe",
[STRING_FEATURE_UNIT] = "Audio Output Volume Control",
[STRING_OUTPUT_TERMINAL] = "Audio Output Terminal",
[STRING_NULL_STREAM] = "Audio Output - Disabled",
[STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo",
};
#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
#define U24(x) U16(x), (((x) >> 16) & 0xff)
#define U32(x) U24(x), (((x) >> 24) & 0xff)
/*
* A Basic Audio Device uses these specific values
*/
#define USBAUDIO_PACKET_SIZE 192
#define USBAUDIO_SAMPLE_RATE 48000
#define USBAUDIO_PACKET_INTERVAL 1
static const USBDescIface desc_iface[] = {
{
.bInterfaceNumber = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
.bInterfaceProtocol = 0x04,
.iInterface = STRING_USBAUDIO_CONTROL,
.ndesc = 4,
.descs = (USBDescOther[]) {
{
/* Headphone Class-Specific AC Interface Header Descriptor */
.data = (uint8_t[]) {
0x09, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AC_HEADER, /* u8 bDescriptorSubtype */
U16(0x0100), /* u16 bcdADC */
U16(0x2b), /* u16 wTotalLength */
0x01, /* u8 bInCollection */
0x01, /* u8 baInterfaceNr */
}
},{
/* Generic Stereo Input Terminal ID1 Descriptor */
.data = (uint8_t[]) {
0x0c, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */
0x01, /* u8 bTerminalID */
U16(0x0101), /* u16 wTerminalType */
0x00, /* u8 bAssocTerminal */
0x02, /* u16 bNrChannels */
U16(0x0003), /* u16 wChannelConfig */
0x00, /* u8 iChannelNames */
STRING_INPUT_TERMINAL, /* u8 iTerminal */
}
},{
/* Generic Stereo Feature Unit ID2 Descriptor */
.data = (uint8_t[]) {
0x0d, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */
0x02, /* u8 bUnitID */
0x01, /* u8 bSourceID */
0x02, /* u8 bControlSize */
U16(0x0001), /* u16 bmaControls(0) */
U16(0x0002), /* u16 bmaControls(1) */
U16(0x0002), /* u16 bmaControls(2) */
STRING_FEATURE_UNIT, /* u8 iFeature */
}
},{
/* Headphone Ouptut Terminal ID3 Descriptor */
.data = (uint8_t[]) {
0x09, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */
0x03, /* u8 bUnitID */
U16(0x0301), /* u16 wTerminalType (SPK) */
0x00, /* u8 bAssocTerminal */
0x02, /* u8 bSourceID */
STRING_OUTPUT_TERMINAL, /* u8 iTerminal */
}
}
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING,
.iInterface = STRING_NULL_STREAM,
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING,
.iInterface = STRING_REAL_STREAM,
.ndesc = 2,
.descs = (USBDescOther[]) {
{
/* Headphone Class-specific AS General Interface Descriptor */
.data = (uint8_t[]) {
0x07, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AS_GENERAL, /* u8 bDescriptorSubtype */
0x01, /* u8 bTerminalLink */
0x00, /* u8 bDelay */
0x01, 0x00, /* u16 wFormatTag */
}
},{
/* Headphone Type I Format Type Descriptor */
.data = (uint8_t[]) {
0x0b, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */
0x01, /* u8 bFormatType */
0x02, /* u8 bNrChannels */
0x02, /* u8 bSubFrameSize */
0x10, /* u8 bBitResolution */
0x01, /* u8 bSamFreqType */
U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */
}
}
},
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | 0x01,
.bmAttributes = 0x0d,
.wMaxPacketSize = USBAUDIO_PACKET_SIZE,
.bInterval = 1,
.is_audio = 1,
/* Stereo Headphone Class-specific
AS Audio Data Endpoint Descriptor */
.extra = (uint8_t[]) {
0x07, /* u8 bLength */
USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */
DST_EP_GENERAL, /* u8 bDescriptorSubtype */
0x00, /* u8 bmAttributes */
0x00, /* u8 bLockDelayUnits */
U16(0x0000), /* u16 wLockDelay */
},
},
}
}
};
static const USBDescDevice desc_device = {
.bcdUSB = 0x0200,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 2,
.bConfigurationValue = DEV_CONFIG_VALUE,
.iConfiguration = STRING_CONFIG,
.bmAttributes = 0xc0,
.bMaxPower = 0x32,
.nif = ARRAY_SIZE(desc_iface),
.ifs = desc_iface,
},
},
};
static const USBDesc desc_audio = {
.id = {
.idVendor = USBAUDIO_VENDOR_NUM,
.idProduct = USBAUDIO_PRODUCT_NUM,
.bcdDevice = 0,
.iManufacturer = STRING_MANUFACTURER,
.iProduct = STRING_PRODUCT,
.iSerialNumber = STRING_SERIALNUMBER,
},
.full = &desc_device,
.str = usb_audio_stringtable,
};
/*
* A USB audio device supports an arbitrary number of alternate
* interface settings for each interface. Each corresponds to a block
* diagram of parameterized blocks. This can thus refer to things like
* number of channels, data rates, or in fact completely different
* block diagrams. Alternative setting 0 is always the null block diagram,
* which is used by a disabled device.
*/
enum usb_audio_altset {
ALTSET_OFF = 0x00, /* No endpoint */
ALTSET_ON = 0x01, /* Single endpoint */
};
/*
* Class-specific control requests
*/
#define CR_SET_CUR 0x01
#define CR_GET_CUR 0x81
#define CR_SET_MIN 0x02
#define CR_GET_MIN 0x82
#define CR_SET_MAX 0x03
#define CR_GET_MAX 0x83
#define CR_SET_RES 0x04
#define CR_GET_RES 0x84
#define CR_SET_MEM 0x05
#define CR_GET_MEM 0x85
#define CR_GET_STAT 0xff
/*
* Feature Unit Control Selectors
*/
#define MUTE_CONTROL 0x01
#define VOLUME_CONTROL 0x02
#define BASS_CONTROL 0x03
#define MID_CONTROL 0x04
#define TREBLE_CONTROL 0x05
#define GRAPHIC_EQUALIZER_CONTROL 0x06
#define AUTOMATIC_GAIN_CONTROL 0x07
#define DELAY_CONTROL 0x08
#define BASS_BOOST_CONTROL 0x09
#define LOUDNESS_CONTROL 0x0a
/*
* buffering
*/
struct streambuf {
uint8_t *data;
uint32_t size;
uint32_t prod;
uint32_t cons;
};
static void streambuf_init(struct streambuf *buf, uint32_t size)
{
g_free(buf->data);
buf->size = size - (size % USBAUDIO_PACKET_SIZE);
buf->data = g_malloc(buf->size);
buf->prod = 0;
buf->cons = 0;
}
static void streambuf_fini(struct streambuf *buf)
{
g_free(buf->data);
buf->data = NULL;
}
static int streambuf_put(struct streambuf *buf, USBPacket *p)
{
uint32_t free = buf->size - (buf->prod - buf->cons);
if (!free) {
return 0;
}
assert(free >= USBAUDIO_PACKET_SIZE);
usb_packet_copy(p, buf->data + (buf->prod % buf->size),
USBAUDIO_PACKET_SIZE);
buf->prod += USBAUDIO_PACKET_SIZE;
return USBAUDIO_PACKET_SIZE;
}
static uint8_t *streambuf_get(struct streambuf *buf)
{
uint32_t used = buf->prod - buf->cons;
uint8_t *data;
if (!used) {
return NULL;
}
assert(used >= USBAUDIO_PACKET_SIZE);
data = buf->data + (buf->cons % buf->size);
buf->cons += USBAUDIO_PACKET_SIZE;
return data;
}
typedef struct USBAudioState {
/* qemu interfaces */
USBDevice dev;
QEMUSoundCard card;
/* state */
struct {
enum usb_audio_altset altset;
struct audsettings as;
SWVoiceOut *voice;
bool mute;
uint8_t vol[2];
struct streambuf buf;
} out;
/* properties */
uint32_t debug;
uint32_t buffer;
} USBAudioState;
static void output_callback(void *opaque, int avail)
{
USBAudioState *s = opaque;
uint8_t *data;
for (;;) {
if (avail < USBAUDIO_PACKET_SIZE) {
return;
}
data = streambuf_get(&s->out.buf);
if (NULL == data) {
return;
}
AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE);
avail -= USBAUDIO_PACKET_SIZE;
}
}
static int usb_audio_set_output_altset(USBAudioState *s, int altset)
{
switch (altset) {
case ALTSET_OFF:
streambuf_init(&s->out.buf, s->buffer);
AUD_set_active_out(s->out.voice, false);
break;
case ALTSET_ON:
AUD_set_active_out(s->out.voice, true);
break;
default:
return -1;
}
if (s->debug) {
fprintf(stderr, "usb-audio: set interface %d\n", altset);
}
s->out.altset = altset;
return 0;
}
/*
* Note: we arbitrarily map the volume control range onto -inf..+8 dB
*/
#define ATTRIB_ID(cs, attrib, idif) \
(((cs) << 24) | ((attrib) << 16) | (idif))
static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
uint16_t cscn, uint16_t idif,
int length, uint8_t *data)
{
uint8_t cs = cscn >> 8;
uint8_t cn = cscn - 1; /* -1 for the non-present master control */
uint32_t aid = ATTRIB_ID(cs, attrib, idif);
int ret = USB_RET_STALL;
switch (aid) {
case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
data[0] = s->out.mute;
ret = 1;
break;
case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
if (cn < 2) {
uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
data[0] = vol;
data[1] = vol >> 8;
ret = 2;
}
break;
case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
if (cn < 2) {
data[0] = 0x01;
data[1] = 0x80;
ret = 2;
}
break;
case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
if (cn < 2) {
data[0] = 0x00;
data[1] = 0x08;
ret = 2;
}
break;
case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
if (cn < 2) {
data[0] = 0x88;
data[1] = 0x00;
ret = 2;
}
break;
}
return ret;
}
static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
uint16_t cscn, uint16_t idif,
int length, uint8_t *data)
{
uint8_t cs = cscn >> 8;
uint8_t cn = cscn - 1; /* -1 for the non-present master control */
uint32_t aid = ATTRIB_ID(cs, attrib, idif);
int ret = USB_RET_STALL;
bool set_vol = false;
switch (aid) {
case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
s->out.mute = data[0] & 1;
set_vol = true;
ret = 0;
break;
case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
if (cn < 2) {
uint16_t vol = data[0] + (data[1] << 8);
if (s->debug) {
fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
}
vol -= 0x8000;
vol = (vol * 255 + 0x4400) / 0x8800;
if (vol > 255) {
vol = 255;
}
s->out.vol[cn] = vol;
set_vol = true;
ret = 0;
}
break;
}
if (set_vol) {
if (s->debug) {
fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
s->out.mute, s->out.vol[0], s->out.vol[1]);
}
AUD_set_volume_out(s->out.voice, s->out.mute,
s->out.vol[0], s->out.vol[1]);
}
return ret;
}
static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index,
int length, uint8_t *data)
{
USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
int ret = 0;
if (s->debug) {
fprintf(stderr, "usb-audio: control transaction: "
"request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
request, value, index, length);
}
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
switch (request) {
case ClassInterfaceRequest | CR_GET_CUR:
case ClassInterfaceRequest | CR_GET_MIN:
case ClassInterfaceRequest | CR_GET_MAX:
case ClassInterfaceRequest | CR_GET_RES:
ret = usb_audio_get_control(s, request & 0xff, value, index,
length, data);
if (ret < 0) {
if (s->debug) {
fprintf(stderr, "usb-audio: fail: get control\n");
}
goto fail;
}
break;
case ClassInterfaceOutRequest | CR_SET_CUR:
case ClassInterfaceOutRequest | CR_SET_MIN:
case ClassInterfaceOutRequest | CR_SET_MAX:
case ClassInterfaceOutRequest | CR_SET_RES:
ret = usb_audio_set_control(s, request & 0xff, value, index,
length, data);
if (ret < 0) {
if (s->debug) {
fprintf(stderr, "usb-audio: fail: set control\n");
}
goto fail;
}
break;
default:
fail:
if (s->debug) {
fprintf(stderr, "usb-audio: failed control transaction: "
"request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
request, value, index, length);
}
ret = USB_RET_STALL;
break;
}
return ret;
}
static void usb_audio_set_interface(USBDevice *dev, int iface,
int old, int value)
{
USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
if (iface == 1) {
usb_audio_set_output_altset(s, value);
}
}
static void usb_audio_handle_reset(USBDevice *dev)
{
USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
if (s->debug) {
fprintf(stderr, "usb-audio: reset\n");
}
usb_audio_set_output_altset(s, ALTSET_OFF);
}
static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
{
int rc;
if (s->out.altset == ALTSET_OFF) {
return USB_RET_STALL;
}
rc = streambuf_put(&s->out.buf, p);
if (rc < p->iov.size && s->debug > 1) {
fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
p->iov.size - rc);
}
return 0;
}
static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
{
USBAudioState *s = (USBAudioState *) dev;
int ret = 0;
switch (p->pid) {
case USB_TOKEN_OUT:
switch (p->devep) {
case 1:
ret = usb_audio_handle_dataout(s, p);
break;
default:
goto fail;
}
break;
default:
fail:
ret = USB_RET_STALL;
break;
}
if (ret == USB_RET_STALL && s->debug) {
fprintf(stderr, "usb-audio: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%zx\n",
p->pid, p->devep, p->iov.size);
}
return ret;
}
static void usb_audio_handle_destroy(USBDevice *dev)
{
USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
if (s->debug) {
fprintf(stderr, "usb-audio: destroy\n");
}
usb_audio_set_output_altset(s, ALTSET_OFF);
AUD_close_out(&s->card, s->out.voice);
AUD_remove_card(&s->card);
streambuf_fini(&s->out.buf);
}
static int usb_audio_initfn(USBDevice *dev)
{
USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
usb_desc_init(dev);
s->dev.opaque = s;
AUD_register_card("usb-audio", &s->card);
s->out.altset = ALTSET_OFF;
s->out.mute = false;
s->out.vol[0] = 240; /* 0 dB */
s->out.vol[1] = 240; /* 0 dB */
s->out.as.freq = USBAUDIO_SAMPLE_RATE;
s->out.as.nchannels = 2;
s->out.as.fmt = AUD_FMT_S16;
s->out.as.endianness = 0;
streambuf_init(&s->out.buf, s->buffer);
s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
s, output_callback, &s->out.as);
AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
AUD_set_active_out(s->out.voice, 0);
return 0;
}
static const VMStateDescription vmstate_usb_audio = {
.name = "usb-audio",
.unmigratable = 1,
};
static struct USBDeviceInfo usb_audio_info = {
.product_desc = "QEMU USB Audio Interface",
.usbdevice_name = "audio",
.qdev.name = "usb-audio",
.qdev.size = sizeof(USBAudioState),
.qdev.vmsd = &vmstate_usb_audio,
.usb_desc = &desc_audio,
.init = usb_audio_initfn,
.handle_packet = usb_generic_handle_packet,
.handle_reset = usb_audio_handle_reset,
.handle_control = usb_audio_handle_control,
.handle_data = usb_audio_handle_data,
.handle_destroy = usb_audio_handle_destroy,
.set_interface = usb_audio_set_interface,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
8 * USBAUDIO_PACKET_SIZE),
DEFINE_PROP_END_OF_LIST(),
}
};
static void usb_audio_register_devices(void)
{
usb_qdev_register(&usb_audio_info);
}
device_init(usb_audio_register_devices)

View File

@ -28,7 +28,6 @@ struct USBBtState {
USBDevice dev;
struct HCIInfo *hci;
int altsetting;
int config;
#define CFIFO_LEN_MASK 255
@ -362,7 +361,6 @@ static void usb_bt_handle_reset(USBDevice *dev)
s->outcmd.len = 0;
s->outacl.len = 0;
s->outsco.len = 0;
s->altsetting = 0;
}
static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
@ -402,26 +400,6 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
case EndpointOutRequest | USB_REQ_SET_FEATURE:
goto fail;
break;
case InterfaceRequest | USB_REQ_GET_INTERFACE:
if (value != 0 || (index & ~1) || length != 1)
goto fail;
if (index == 1)
data[0] = s->altsetting;
else
data[0] = 0;
ret = 1;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
if ((index & ~1) || length != 0 ||
(index == 1 && (value < 0 || value > 4)) ||
(index == 0 && value != 0)) {
printf("%s: Wrong SET_INTERFACE request (%i, %i)\n",
__FUNCTION__, index, value);
goto fail;
}
s->altsetting = value;
ret = 0;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
if (s->config)
usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,

View File

@ -75,6 +75,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
dev->info = info;
dev->auto_attach = 1;
QLIST_INIT(&dev->strings);
usb_ep_init(dev);
rc = usb_claim_port(dev);
if (rc != 0) {
return rc;

View File

@ -611,14 +611,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
}
switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
/* Class specific requests. */
case InterfaceOutClass | CCID_CONTROL_ABORT:
DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");

View File

@ -192,9 +192,10 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x07;
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
if (len < bLength) {
if (len < bLength + extralen) {
return -1;
}
@ -205,8 +206,15 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
dest[0x04] = usb_lo(ep->wMaxPacketSize);
dest[0x05] = usb_hi(ep->wMaxPacketSize);
dest[0x06] = ep->bInterval;
if (ep->is_audio) {
dest[0x07] = ep->bRefresh;
dest[0x08] = ep->bSynchAddress;
}
if (ep->extra) {
memcpy(dest + bLength, ep->extra, extralen);
}
return bLength;
return bLength + extralen;
}
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
@ -223,6 +231,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
/* ------------------------------------------------------------------ */
static void usb_desc_ep_init(USBDevice *dev)
{
const USBDescIface *iface;
int i, e, pid, ep;
usb_ep_init(dev);
for (i = 0; i < dev->ninterfaces; i++) {
iface = dev->ifaces[i];
if (iface == NULL) {
continue;
}
for (e = 0; e < iface->bNumEndpoints; e++) {
pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
USB_TOKEN_IN : USB_TOKEN_OUT;
ep = iface->eps[e].bEndpointAddress & 0x0f;
usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
usb_ep_set_max_packet_size(dev, pid, ep,
iface->eps[e].wMaxPacketSize);
}
}
}
static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
int nif, int alt)
{
const USBDescIface *iface;
int g, i;
if (!dev->config) {
return NULL;
}
for (g = 0; g < dev->config->nif_groups; g++) {
for (i = 0; i < dev->config->if_groups[g].nif; i++) {
iface = &dev->config->if_groups[g].ifs[i];
if (iface->bInterfaceNumber == nif &&
iface->bAlternateSetting == alt) {
return iface;
}
}
}
for (i = 0; i < dev->config->nif; i++) {
iface = &dev->config->ifs[i];
if (iface->bInterfaceNumber == nif &&
iface->bAlternateSetting == alt) {
return iface;
}
}
return NULL;
}
static int usb_desc_set_interface(USBDevice *dev, int index, int value)
{
const USBDescIface *iface;
int old;
iface = usb_desc_find_interface(dev, index, value);
if (iface == NULL) {
return -1;
}
old = dev->altsetting[index];
dev->altsetting[index] = value;
dev->ifaces[index] = iface;
usb_desc_ep_init(dev);
if (dev->info->set_interface && old != value) {
dev->info->set_interface(dev, index, old, value);
}
return 0;
}
static int usb_desc_set_config(USBDevice *dev, int value)
{
int i;
if (value == 0) {
dev->configuration = 0;
dev->ninterfaces = 0;
dev->config = NULL;
} else {
for (i = 0; i < dev->device->bNumConfigurations; i++) {
if (dev->device->confs[i].bConfigurationValue == value) {
dev->configuration = value;
dev->ninterfaces = dev->device->confs[i].bNumInterfaces;
dev->config = dev->device->confs + i;
assert(dev->ninterfaces <= USB_MAX_INTERFACES);
}
}
if (i < dev->device->bNumConfigurations) {
return -1;
}
}
for (i = 0; i < dev->ninterfaces; i++) {
usb_desc_set_interface(dev, i, 0);
}
for (; i < USB_MAX_INTERFACES; i++) {
dev->altsetting[i] = 0;
dev->ifaces[i] = NULL;
}
return 0;
}
static void usb_desc_setdefaults(USBDevice *dev)
{
const USBDesc *desc = dev->info->usb_desc;
@ -237,7 +350,7 @@ static void usb_desc_setdefaults(USBDevice *dev)
dev->device = desc->high;
break;
}
dev->config = dev->device->confs;
usb_desc_set_config(dev, 0);
}
void usb_desc_init(USBDevice *dev)
@ -408,7 +521,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
const USBDesc *desc = dev->info->usb_desc;
int i, ret = -1;
int ret = -1;
assert(desc != NULL);
switch(request) {
@ -427,12 +540,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
for (i = 0; i < dev->device->bNumConfigurations; i++) {
if (dev->device->confs[i].bConfigurationValue == value) {
dev->config = dev->device->confs + i;
ret = 0;
}
}
ret = usb_desc_set_config(dev, value);
trace_usb_set_config(dev->addr, value, ret);
break;
@ -461,6 +569,19 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
}
trace_usb_set_device_feature(dev->addr, value, ret);
break;
case InterfaceRequest | USB_REQ_GET_INTERFACE:
if (index < 0 || index >= dev->ninterfaces) {
break;
}
data[0] = dev->altsetting[index];
ret = 1;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = usb_desc_set_interface(dev, index, value);
trace_usb_set_interface(dev->addr, index, value, ret);
break;
}
return ret;
}

View File

@ -71,6 +71,11 @@ struct USBDescEndpoint {
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
uint8_t bRefresh;
uint8_t bSynchAddress;
uint8_t is_audio; /* has bRefresh + bSynchAddress */
uint8_t *extra;
};
struct USBDescOther {

View File

@ -715,7 +715,8 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev)
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
if (q->packet.owner != dev) {
if (q->packet.owner == NULL ||
q->packet.owner->dev != dev) {
continue;
}
ehci_free_queue(q);

View File

@ -384,13 +384,6 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
ret = 0;
switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
/* hid specific requests */
case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
switch (value >> 8) {

View File

@ -258,13 +258,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
}
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
/* usb specific requests */
case GetHubStatus:
data[0] = 0;

View File

@ -306,19 +306,9 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
ret = 0;
switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
ret = 0;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
/* Class specific requests. */
case ClassInterfaceOutRequest | MassStorageReset:
/* Reset state ready for the next CBW. */

View File

@ -812,7 +812,8 @@ static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
for (ep = 0; ep < 16; ep++) {
for (dir = 0; dir < 2; dir++) {
if (s->ep[ep].packey[dir].p.owner != dev) {
if (s->ep[ep].packey[dir].p.owner == NULL ||
s->ep[ep].packey[dir].p.owner->dev != dev) {
continue;
}
usb_cancel_packet(&s->ep[ep].packey[dir].p);

View File

@ -71,9 +71,6 @@ enum usbstring_idx {
#define USB_CDC_UNION_TYPE 0x06 /* union_desc */
#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_CS_ENDPOINT 0x25
#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00
#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01
#define USB_CDC_REQ_SET_LINE_CODING 0x20
@ -1098,17 +1095,6 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
#endif
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
case InterfaceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
default:
fail:
fprintf(stderr, "usbnet: failed control transaction: "

View File

@ -1707,7 +1707,9 @@ static void ohci_mem_write(void *opaque,
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
{
if (ohci->async_td && ohci->usb_packet.owner == dev) {
if (ohci->async_td &&
ohci->usb_packet.owner != NULL &&
ohci->usb_packet.owner->dev == dev) {
usb_cancel_packet(&ohci->usb_packet);
ohci->async_td = 0;
}

View File

@ -233,13 +233,6 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
ret = 0;
switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
ret = 0;
break;

View File

@ -245,7 +245,8 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
UHCIAsync *curr, *n;
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
if (curr->packet.owner != dev) {
if (curr->packet.owner == NULL ||
curr->packet.owner->dev != dev) {
continue;
}
uhci_async_unlink(s, curr);

View File

@ -263,13 +263,6 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
ret = 0;
switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
case WACOM_SET_REPORT:
if (s->mouse_grabbed) {
qemu_remove_mouse_event_handler(s->eh_entry);

2749
hw/usb-xhci.c Normal file

File diff suppressed because it is too large Load Diff

125
hw/usb.c
View File

@ -329,7 +329,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
ret = dev->info->handle_packet(dev, p);
if (ret == USB_RET_ASYNC) {
if (p->owner == NULL) {
p->owner = dev;
p->owner = usb_ep_get(dev, p->pid, p->devep);
} else {
/* We'll end up here when usb_handle_packet is called
* recursively due to a hub being in the chain. Nothing
@ -357,7 +357,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
void usb_cancel_packet(USBPacket * p)
{
assert(p->owner != NULL);
p->owner->info->cancel_packet(p->owner, p);
p->owner->dev->info->cancel_packet(p->owner->dev, p);
p->owner = NULL;
}
@ -414,3 +414,124 @@ void usb_packet_cleanup(USBPacket *p)
{
qemu_iovec_destroy(&p->iov);
}
void usb_ep_init(USBDevice *dev)
{
int ep;
dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
dev->ep_ctl.ifnum = 0;
dev->ep_ctl.dev = dev;
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_in[ep].ifnum = 0;
dev->ep_out[ep].ifnum = 0;
dev->ep_in[ep].dev = dev;
dev->ep_out[ep].dev = dev;
}
}
void usb_ep_dump(USBDevice *dev)
{
static const char *tname[] = {
[USB_ENDPOINT_XFER_CONTROL] = "control",
[USB_ENDPOINT_XFER_ISOC] = "isoc",
[USB_ENDPOINT_XFER_BULK] = "bulk",
[USB_ENDPOINT_XFER_INT] = "int",
};
int ifnum, ep, first;
fprintf(stderr, "Device \"%s\", config %d\n",
dev->product_desc, dev->configuration);
for (ifnum = 0; ifnum < 16; ifnum++) {
first = 1;
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
dev->ep_in[ep].ifnum == ifnum) {
if (first) {
first = 0;
fprintf(stderr, " Interface %d, alternative %d\n",
ifnum, dev->altsetting[ifnum]);
}
fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep,
tname[dev->ep_in[ep].type],
dev->ep_in[ep].max_packet_size);
}
if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
dev->ep_out[ep].ifnum == ifnum) {
if (first) {
first = 0;
fprintf(stderr, " Interface %d, alternative %d\n",
ifnum, dev->altsetting[ifnum]);
}
fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep,
tname[dev->ep_out[ep].type],
dev->ep_out[ep].max_packet_size);
}
}
}
fprintf(stderr, "--\n");
}
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out;
if (ep == 0) {
return &dev->ep_ctl;
}
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
return eps + ep - 1;
}
uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
return uep->type;
}
void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->type = type;
}
uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
return uep->ifnum;
}
void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->ifnum = ifnum;
}
void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
int size, microframes;
size = raw & 0x7ff;
switch ((raw >> 11) & 3) {
case 1:
microframes = 2;
break;
case 2:
microframes = 3;
break;
default:
microframes = 1;
break;
}
uep->max_packet_size = size * microframes;
}
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
return uep->max_packet_size;
}

View File

@ -79,6 +79,11 @@
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
#define USB_SUBCLASS_UNDEFINED 0
#define USB_SUBCLASS_AUDIO_CONTROL 1
#define USB_SUBCLASS_AUDIO_STREAMING 2
#define USB_SUBCLASS_AUDIO_MIDISTREAMING 3
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
@ -132,11 +137,14 @@
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_DEBUG 0x0A
#define USB_DT_INTERFACE_ASSOC 0x0B
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_CS_ENDPOINT 0x25
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_XFER_INVALID 255
typedef struct USBBus USBBus;
typedef struct USBBusOps USBBusOps;
@ -144,6 +152,7 @@ typedef struct USBPort USBPort;
typedef struct USBDevice USBDevice;
typedef struct USBDeviceInfo USBDeviceInfo;
typedef struct USBPacket USBPacket;
typedef struct USBEndpoint USBEndpoint;
typedef struct USBDesc USBDesc;
typedef struct USBDescID USBDescID;
@ -161,6 +170,16 @@ struct USBDescString {
QLIST_ENTRY(USBDescString) next;
};
#define USB_MAX_ENDPOINTS 15
#define USB_MAX_INTERFACES 16
struct USBEndpoint {
uint8_t type;
uint8_t ifnum;
int max_packet_size;
USBDevice *dev;
};
/* definition of a USB device */
struct USBDevice {
DeviceState qdev;
@ -186,9 +205,18 @@ struct USBDevice {
int32_t setup_len;
int32_t setup_index;
USBEndpoint ep_ctl;
USBEndpoint ep_in[USB_MAX_ENDPOINTS];
USBEndpoint ep_out[USB_MAX_ENDPOINTS];
QLIST_HEAD(, USBDescString) strings;
const USBDescDevice *device;
int configuration;
int ninterfaces;
int altsetting[USB_MAX_INTERFACES];
const USBDescConfig *config;
const USBDescIface *ifaces[USB_MAX_INTERFACES];
};
struct USBDeviceInfo {
@ -241,6 +269,9 @@ struct USBDeviceInfo {
*/
int (*handle_data)(USBDevice *dev, USBPacket *p);
void (*set_interface)(USBDevice *dev, int interface,
int alt_old, int alt_new);
const char *product_desc;
const USBDesc *usb_desc;
@ -288,7 +319,7 @@ struct USBPacket {
QEMUIOVector iov;
int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
USBDevice *owner;
USBEndpoint *owner;
};
void usb_packet_init(USBPacket *p);
@ -304,6 +335,17 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
void usb_ep_init(USBDevice *dev);
void usb_ep_dump(USBDevice *dev);
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep);
void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type);
void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum);
void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
void usb_reset(USBPort *port);

View File

@ -246,6 +246,7 @@ usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query
usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
usb_set_addr(int addr) "dev %d"
usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"
usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"

View File

@ -66,27 +66,11 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
#define DPRINTF(...)
#endif
#define USBDBG_DEVOPENED "husb: opened %s/devices\n"
#define USBPROCBUS_PATH "/proc/bus/usb"
#define PRODUCT_NAME_SZ 32
#define MAX_ENDPOINTS 15
#define MAX_PORTLEN 16
#define USBDEVBUS_PATH "/dev/bus/usb"
#define USBSYSBUS_PATH "/sys/bus/usb"
static char *usb_host_device_path;
#define USB_FS_NONE 0
#define USB_FS_PROC 1
#define USB_FS_DEV 2
#define USB_FS_SYS 3
static int usb_fs_type;
/* endpoint association data */
#define ISO_FRAME_DESC_PER_URB 32
#define INVALID_EP_TYPE 255
/* devio.c limits single requests to 16k */
#define MAX_USBFS_BUFFER_SIZE 16384
@ -94,13 +78,11 @@ static int usb_fs_type;
typedef struct AsyncURB AsyncURB;
struct endp_data {
uint8_t type;
uint8_t halted;
uint8_t iso_started;
AsyncURB *iso_urb;
int iso_urb_idx;
int iso_buffer_used;
int max_packet_size;
int inflight;
};
@ -120,14 +102,12 @@ typedef struct USBHostDevice {
uint8_t descr[8192];
int descr_len;
int configuration;
int ninterfaces;
int closing;
uint32_t iso_urb_count;
Notifier exit;
struct endp_data ep_in[MAX_ENDPOINTS];
struct endp_data ep_out[MAX_ENDPOINTS];
struct endp_data ep_in[USB_MAX_ENDPOINTS];
struct endp_data ep_out[USB_MAX_ENDPOINTS];
QLIST_HEAD(, AsyncURB) aurbs;
/* Host side address */
@ -149,6 +129,19 @@ static int usb_host_read_file(char *line, size_t line_size,
const char *device_file, const char *device_name);
static int usb_linux_update_endp_table(USBHostDevice *s);
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,
};
uint8_t type = usb_ep_get_type(&s->dev, p->pid, p->devep);
assert(type < ARRAY_SIZE(usbfs));
return usbfs[type];
}
static int usb_host_do_reset(USBHostDevice *dev)
{
struct timeval s, e;
@ -172,18 +165,18 @@ static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
{
struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
assert(ep > 0 && ep <= MAX_ENDPOINTS);
assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
return eps + ep - 1;
}
static int is_isoc(USBHostDevice *s, int pid, int ep)
{
return get_endp(s, pid, ep)->type == USBDEVFS_URB_TYPE_ISO;
return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
}
static int is_valid(USBHostDevice *s, int pid, int ep)
{
return get_endp(s, pid, ep)->type != INVALID_EP_TYPE;
return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
}
static int is_halted(USBHostDevice *s, int pid, int ep)
@ -265,26 +258,6 @@ static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
return get_endp(s, pid, ep)->iso_buffer_used;
}
static void set_max_packet_size(USBHostDevice *s, int pid, int ep,
uint8_t *descriptor)
{
int raw = descriptor[4] + (descriptor[5] << 8);
int size, microframes;
size = raw & 0x7ff;
switch ((raw >> 11) & 3) {
case 1: microframes = 2; break;
case 2: microframes = 3; break;
default: microframes = 1; break;
}
get_endp(s, pid, ep)->max_packet_size = size * microframes;
}
static int get_max_packet_size(USBHostDevice *s, int pid, int ep)
{
return get_endp(s, pid, ep)->max_packet_size;
}
/*
* Async URB state.
* We always allocate iso packet descriptors even for bulk transfers
@ -431,6 +404,31 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
}
}
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;
}
static int usb_host_claim_port(USBHostDevice *s)
{
#ifdef USBDEVFS_CLAIM_PORT
@ -460,12 +458,7 @@ static int usb_host_claim_port(USBHostDevice *s)
return -1;
}
if (!usb_host_device_path) {
return -1;
}
snprintf(line, sizeof(line), "%s/%03d/%03d",
usb_host_device_path, s->match.bus_num, hub_addr);
s->hub_fd = open(line, O_RDWR | O_NONBLOCK);
s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
if (s->hub_fd < 0) {
return -1;
}
@ -522,10 +515,6 @@ static int usb_linux_get_num_interfaces(USBHostDevice *s)
char device_name[64], line[1024];
int num_interfaces = 0;
if (usb_fs_type != USB_FS_SYS) {
return -1;
}
sprintf(device_name, "%d-%s", s->bus_num, s->port);
if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
device_name)) {
@ -544,9 +533,13 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
int interface, nb_interfaces;
int ret, i;
for (i = 0; i < USB_MAX_INTERFACES; i++) {
dev->dev.altsetting[i] = 0;
}
if (configuration == 0) { /* address state - ignore */
dev->ninterfaces = 0;
dev->configuration = 0;
dev->dev.ninterfaces = 0;
dev->dev.configuration = 0;
return 1;
}
@ -604,8 +597,8 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
nb_interfaces, configuration);
dev->ninterfaces = nb_interfaces;
dev->configuration = configuration;
dev->dev.ninterfaces = nb_interfaces;
dev->dev.configuration = configuration;
return 1;
fail:
@ -622,7 +615,7 @@ static int usb_host_release_interfaces(USBHostDevice *s)
trace_usb_host_release_interfaces(s->bus_num, s->addr);
for (i = 0; i < s->ninterfaces; i++) {
for (i = 0; i < s->dev.ninterfaces; i++) {
ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
if (ret < 0) {
perror("USBDEVFS_RELEASEINTERFACE");
@ -660,7 +653,7 @@ static void usb_host_handle_destroy(USBDevice *dev)
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
{
AsyncURB *aurb;
int i, j, len = get_max_packet_size(s, pid, ep);
int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
for (i = 0; i < s->iso_urb_count; i++) {
@ -740,7 +733,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
int i, j, ret, max_packet_size, offset, len = 0;
uint8_t *buf;
max_packet_size = get_max_packet_size(s, p->pid, p->devep);
max_packet_size = usb_ep_get_max_packet_size(&s->dev, p->pid, p->devep);
if (max_packet_size == 0)
return USB_RET_NAK;
@ -892,7 +885,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
urb = &aurb->urb;
urb->endpoint = ep;
urb->type = USBDEVFS_URB_TYPE_BULK;
urb->type = usb_host_usbfs_type(s, p);
urb->usercontext = s;
urb->buffer = pbuf;
urb->buffer_length = prem;
@ -988,7 +981,7 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
for (i = 1; i <= MAX_ENDPOINTS; i++) {
for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
if (is_isoc(s, USB_TOKEN_IN, i)) {
usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
}
@ -997,6 +990,10 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
}
}
if (iface >= USB_MAX_INTERFACES) {
return USB_RET_STALL;
}
si.interface = iface;
si.altsetting = alt;
ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
@ -1007,6 +1004,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
if (ret < 0) {
return ctrl_error();
}
s->dev.altsetting[iface] = alt;
usb_linux_update_endp_table(s);
return 0;
}
@ -1090,41 +1089,21 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
uint8_t configuration, uint8_t interface)
{
uint8_t alt_setting;
struct usb_ctrltransfer ct;
int ret;
char device_name[64], line[1024];
int alt_setting;
if (usb_fs_type == USB_FS_SYS) {
char device_name[64], line[1024];
int alt_setting;
sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
(int)configuration, (int)interface);
sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
(int)configuration, (int)interface);
if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
device_name)) {
goto usbdevfs;
}
if (sscanf(line, "%d", &alt_setting) != 1) {
goto usbdevfs;
}
return alt_setting;
}
usbdevfs:
ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
ct.bRequest = USB_REQ_GET_INTERFACE;
ct.wValue = 0;
ct.wIndex = interface;
ct.wLength = 1;
ct.data = &alt_setting;
ct.timeout = 50;
ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
if (ret < 0) {
if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
device_name)) {
/* Assume alt 0 on error */
return 0;
}
if (sscanf(line, "%d", &alt_setting) != 1) {
/* Assume alt 0 on error */
return 0;
}
return alt_setting;
}
@ -1133,15 +1112,13 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
{
uint8_t *descriptors;
uint8_t devep, type, alt_interface;
uint16_t raw;
int interface, length, i, ep, pid;
struct endp_data *epd;
for (i = 0; i < MAX_ENDPOINTS; i++) {
s->ep_in[i].type = INVALID_EP_TYPE;
s->ep_out[i].type = INVALID_EP_TYPE;
}
usb_ep_init(&s->dev);
if (s->configuration == 0) {
if (s->dev.configuration == 0) {
/* not configured yet -- leave all endpoints disabled */
return 0;
}
@ -1156,12 +1133,11 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
if (descriptors[i + 1] != USB_DT_CONFIG) {
fprintf(stderr, "invalid descriptor data\n");
return 1;
} else if (descriptors[i + 5] != s->configuration) {
DPRINTF("not requested configuration %d\n", s->configuration);
} else if (descriptors[i + 5] != s->dev.configuration) {
DPRINTF("not requested configuration %d\n", s->dev.configuration);
i += (descriptors[i + 3] << 8) + descriptors[i + 2];
continue;
}
i += descriptors[i];
if (descriptors[i + 1] != USB_DT_INTERFACE ||
@ -1172,7 +1148,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
}
interface = descriptors[i + 2];
alt_interface = usb_linux_get_alt_setting(s, s->configuration,
alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
interface);
/* the current interface descriptor is the active interface
@ -1203,32 +1179,23 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
return 1;
}
switch (descriptors[i + 3] & 0x3) {
case 0x00:
type = USBDEVFS_URB_TYPE_CONTROL;
break;
case 0x01:
type = USBDEVFS_URB_TYPE_ISO;
set_max_packet_size(s, pid, ep, descriptors + i);
break;
case 0x02:
type = USBDEVFS_URB_TYPE_BULK;
break;
case 0x03:
type = USBDEVFS_URB_TYPE_INTERRUPT;
break;
default:
DPRINTF("usb_host: malformed endpoint type\n");
type = USBDEVFS_URB_TYPE_BULK;
}
type = descriptors[i + 3] & 0x3;
raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
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);
epd = get_endp(s, pid, ep);
assert(epd->type == INVALID_EP_TYPE);
epd->type = type;
epd->halted = 0;
i += descriptors[i];
}
}
#ifdef DEBUG
usb_ep_dump(&s->dev);
#endif
return 0;
}
@ -1273,7 +1240,6 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
const char *prod_name, int speed)
{
int fd = -1, ret;
char buf[1024];
trace_usb_host_open_started(bus_num, addr);
@ -1281,15 +1247,8 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
goto fail;
}
if (!usb_host_device_path) {
perror("husb: USB Host Device Path not set");
goto fail;
}
snprintf(buf, sizeof(buf), "%s/%03d/%03d", usb_host_device_path,
bus_num, addr);
fd = open(buf, O_RDWR | O_NONBLOCK);
fd = usb_host_open_device(bus_num, addr);
if (fd < 0) {
perror(buf);
goto fail;
}
DPRINTF("husb: opened %s\n", buf);
@ -1390,7 +1349,7 @@ static int usb_host_close(USBHostDevice *dev)
qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
dev->closing = 1;
for (i = 1; i <= MAX_ENDPOINTS; i++) {
for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
if (is_isoc(dev, USB_TOKEN_IN, i)) {
usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
}
@ -1538,149 +1497,6 @@ int usb_host_device_close(const char *devname)
return -1;
}
static int get_tag_value(char *buf, int buf_size,
const char *str, const char *tag,
const char *stopchars)
{
const char *p;
char *q;
p = strstr(str, tag);
if (!p) {
return -1;
}
p += strlen(tag);
while (qemu_isspace(*p)) {
p++;
}
q = buf;
while (*p != '\0' && !strchr(stopchars, *p)) {
if ((q - buf) < (buf_size - 1)) {
*q++ = *p;
}
p++;
}
*q = '\0';
return q - buf;
}
/*
* Use /proc/bus/usb/devices or /dev/bus/usb/devices file to determine
* host's USB devices. This is legacy support since many distributions
* are moving to /sys/bus/usb
*/
static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
{
FILE *f = NULL;
char line[1024];
char buf[1024];
int bus_num, addr, speed, device_count;
int class_id, product_id, vendor_id, port;
char product_name[512];
int ret = 0;
if (!usb_host_device_path) {
perror("husb: USB Host Device Path not set");
goto the_end;
}
snprintf(line, sizeof(line), "%s/devices", usb_host_device_path);
f = fopen(line, "r");
if (!f) {
perror("husb: cannot open devices file");
goto the_end;
}
device_count = 0;
bus_num = addr = class_id = product_id = vendor_id = port = 0;
speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */
for(;;) {
if (fgets(line, sizeof(line), f) == NULL) {
break;
}
if (strlen(line) > 0) {
line[strlen(line) - 1] = '\0';
}
if (line[0] == 'T' && line[1] == ':') {
if (device_count && (vendor_id || product_id)) {
/* New device. Add the previously discovered device. */
if (port > 0) {
snprintf(buf, sizeof(buf), "%d", port);
} else {
snprintf(buf, sizeof(buf), "?");
}
ret = func(opaque, bus_num, addr, buf, class_id, vendor_id,
product_id, product_name, speed);
if (ret) {
goto the_end;
}
}
if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) {
goto fail;
}
bus_num = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Port=", " ") < 0) {
goto fail;
}
port = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) {
goto fail;
}
addr = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) {
goto fail;
}
if (!strcmp(buf, "5000")) {
speed = USB_SPEED_SUPER;
} else if (!strcmp(buf, "480")) {
speed = USB_SPEED_HIGH;
} else if (!strcmp(buf, "1.5")) {
speed = USB_SPEED_LOW;
} else {
speed = USB_SPEED_FULL;
}
product_name[0] = '\0';
class_id = 0xff;
device_count++;
product_id = 0;
vendor_id = 0;
} else if (line[0] == 'P' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) {
goto fail;
}
vendor_id = strtoul(buf, NULL, 16);
if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) {
goto fail;
}
product_id = strtoul(buf, NULL, 16);
} else if (line[0] == 'S' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) {
goto fail;
}
pstrcpy(product_name, sizeof(product_name), buf);
} else if (line[0] == 'D' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) {
goto fail;
}
class_id = strtoul(buf, NULL, 16);
}
fail: ;
}
if (device_count && (vendor_id || product_id)) {
/* Add the last device. */
if (port > 0) {
snprintf(buf, sizeof(buf), "%d", port);
} else {
snprintf(buf, sizeof(buf), "?");
}
ret = func(opaque, bus_num, addr, buf, class_id, vendor_id,
product_id, product_name, speed);
}
the_end:
if (f) {
fclose(f);
}
return ret;
}
/*
* Read sys file-system device file
*
@ -1698,7 +1514,7 @@ static int usb_host_read_file(char *line, size_t line_size,
int ret = 0;
char filename[PATH_MAX];
snprintf(filename, PATH_MAX, USBSYSBUS_PATH "/devices/%s/%s", device_name,
snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
device_file);
f = fopen(filename, "r");
if (f) {
@ -1716,7 +1532,7 @@ static int usb_host_read_file(char *line, size_t line_size,
* 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
*/
static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
static int usb_host_scan(void *opaque, USBScanFunc *func)
{
DIR *dir = NULL;
char line[1024];
@ -1726,9 +1542,10 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
char product_name[512];
struct dirent *de;
dir = opendir(USBSYSBUS_PATH "/devices");
dir = opendir("/sys/bus/usb/devices");
if (!dir) {
perror("husb: cannot open devices directory");
perror("husb: opendir /sys/bus/usb/devices");
fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
goto the_end;
}
@ -1803,81 +1620,6 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
return ret;
}
/*
* Determine how to access the host's USB devices and call the
* specific support function.
*/
static int usb_host_scan(void *opaque, USBScanFunc *func)
{
Monitor *mon = cur_mon;
FILE *f = NULL;
DIR *dir = NULL;
int ret = 0;
const char *fs_type[] = {"unknown", "proc", "dev", "sys"};
char devpath[PATH_MAX];
/* only check the host once */
if (!usb_fs_type) {
dir = opendir(USBSYSBUS_PATH "/devices");
if (dir) {
/* devices found in /dev/bus/usb/ (yes - not a mistake!) */
strcpy(devpath, USBDEVBUS_PATH);
usb_fs_type = USB_FS_SYS;
closedir(dir);
DPRINTF(USBDBG_DEVOPENED, USBSYSBUS_PATH);
goto found_devices;
}
f = fopen(USBPROCBUS_PATH "/devices", "r");
if (f) {
/* devices found in /proc/bus/usb/ */
strcpy(devpath, USBPROCBUS_PATH);
usb_fs_type = USB_FS_PROC;
fclose(f);
DPRINTF(USBDBG_DEVOPENED, USBPROCBUS_PATH);
goto found_devices;
}
/* try additional methods if an access method hasn't been found yet */
f = fopen(USBDEVBUS_PATH "/devices", "r");
if (f) {
/* devices found in /dev/bus/usb/ */
strcpy(devpath, USBDEVBUS_PATH);
usb_fs_type = USB_FS_DEV;
fclose(f);
DPRINTF(USBDBG_DEVOPENED, USBDEVBUS_PATH);
goto found_devices;
}
found_devices:
if (!usb_fs_type) {
if (mon) {
monitor_printf(mon, "husb: unable to access USB devices\n");
}
return -ENOENT;
}
/* the module setting (used later for opening devices) */
usb_host_device_path = g_malloc0(strlen(devpath)+1);
strcpy(usb_host_device_path, devpath);
if (mon) {
monitor_printf(mon, "husb: using %s file-system with %s\n",
fs_type[usb_fs_type], usb_host_device_path);
}
}
switch (usb_fs_type) {
case USB_FS_PROC:
case USB_FS_DEV:
ret = usb_host_scan_dev(opaque, func);
break;
case USB_FS_SYS:
ret = usb_host_scan_sys(opaque, func);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static QEMUTimer *usb_auto_timer;
static int usb_host_auto_scan(void *opaque, int bus_num,

View File

@ -60,7 +60,11 @@ struct endp_data {
uint8_t iso_error; /* For reporting iso errors to the HC */
uint8_t interrupt_started;
uint8_t interrupt_error;
uint8_t bufpq_prefilled;
uint8_t bufpq_dropping_packets;
QTAILQ_HEAD(, buf_packet) bufpq;
int bufpq_size;
int bufpq_target_size;
};
struct USBRedirDevice {
@ -287,21 +291,41 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
}
}
static struct buf_packet *bufp_alloc(USBRedirDevice *dev,
static void bufp_alloc(USBRedirDevice *dev,
uint8_t *data, int len, int status, uint8_t ep)
{
struct buf_packet *bufp = g_malloc(sizeof(struct buf_packet));
struct buf_packet *bufp;
if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
dev->endpoint[EP2I(ep)].bufpq_size >
2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
}
/* Since we're interupting the stream anyways, drop enough packets to get
back to our target buffer size */
if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
if (dev->endpoint[EP2I(ep)].bufpq_size >
dev->endpoint[EP2I(ep)].bufpq_target_size) {
free(data);
return;
}
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
bufp = g_malloc(sizeof(struct buf_packet));
bufp->data = data;
bufp->len = len;
bufp->status = status;
QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
return bufp;
dev->endpoint[EP2I(ep)].bufpq_size++;
}
static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
uint8_t ep)
{
QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
dev->endpoint[EP2I(ep)].bufpq_size--;
free(bufp->data);
g_free(bufp);
}
@ -332,35 +356,77 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep)
{
int status, len;
if (!dev->endpoint[EP2I(ep)].iso_started &&
!dev->endpoint[EP2I(ep)].iso_error) {
struct usb_redir_start_iso_stream_header start_iso = {
.endpoint = ep,
/* TODO maybe do something with these depending on ep interval? */
.pkts_per_urb = 32,
.no_urbs = 3,
};
int pkts_per_sec;
if (dev->dev.speed == USB_SPEED_HIGH) {
pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
} else {
pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
}
/* Testing has shown that we need circa 60 ms buffer */
dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
/* Aim for approx 100 interrupts / second on the client to
balance latency and interrupt load */
start_iso.pkts_per_urb = pkts_per_sec / 100;
if (start_iso.pkts_per_urb < 1) {
start_iso.pkts_per_urb = 1;
} else if (start_iso.pkts_per_urb > 32) {
start_iso.pkts_per_urb = 32;
}
start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
start_iso.pkts_per_urb - 1) /
start_iso.pkts_per_urb;
/* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
as overflow buffer. Also see the usbredir protocol documentation */
if (!(ep & USB_DIR_IN)) {
start_iso.no_urbs *= 2;
}
if (start_iso.no_urbs > 16) {
start_iso.no_urbs = 16;
}
/* No id, we look at the ep when receiving a status back */
usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
usbredirparser_do_write(dev->parser);
DPRINTF("iso stream started ep %02X\n", ep);
DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
dev->endpoint[EP2I(ep)].iso_started = 1;
dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
if (ep & USB_DIR_IN) {
struct buf_packet *isop;
if (dev->endpoint[EP2I(ep)].iso_started &&
!dev->endpoint[EP2I(ep)].bufpq_prefilled) {
if (dev->endpoint[EP2I(ep)].bufpq_size <
dev->endpoint[EP2I(ep)].bufpq_target_size) {
return usbredir_handle_status(dev, 0, 0);
}
dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
}
isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
if (isop == NULL) {
DPRINTF2("iso-token-in ep %02X, no isop\n", ep);
DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
ep, dev->endpoint[EP2I(ep)].iso_error);
/* Re-fill the buffer */
dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
/* Check iso_error for stream errors, otherwise its an underrun */
status = dev->endpoint[EP2I(ep)].iso_error;
dev->endpoint[EP2I(ep)].iso_error = 0;
return usbredir_handle_status(dev, status, 0);
}
DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status,
isop->len);
DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
status = isop->status;
if (status != usb_redir_success) {
@ -370,7 +436,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
len = isop->len;
if (len > p->iov.size) {
ERROR("received iso data is larger then packet ep %02X\n", ep);
ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
ep, len, (int)p->iov.size);
bufp_free(dev, isop, ep);
return USB_RET_NAK;
}
@ -410,6 +477,7 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
DPRINTF("iso stream stopped ep %02X\n", ep);
dev->endpoint[EP2I(ep)].iso_started = 0;
}
dev->endpoint[EP2I(ep)].iso_error = 0;
usbredir_free_bufpq(dev, ep);
}
@ -460,6 +528,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
usbredirparser_do_write(dev->parser);
DPRINTF("interrupt recv started ep %02X\n", ep);
dev->endpoint[EP2I(ep)].interrupt_started = 1;
/* We don't really want to drop interrupt packets ever, but
having some upper limit to how much we buffer is good. */
dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
@ -522,6 +594,7 @@ static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
DPRINTF("interrupt recv stopped ep %02X\n", ep);
dev->endpoint[EP2I(ep)].interrupt_started = 0;
}
dev->endpoint[EP2I(ep)].interrupt_error = 0;
usbredir_free_bufpq(dev, ep);
}
@ -959,9 +1032,24 @@ static void usbredir_ep_info(void *priv,
dev->endpoint[i].type = ep_info->type[i];
dev->endpoint[i].interval = ep_info->interval[i];
dev->endpoint[i].interface = ep_info->interface[i];
if (dev->endpoint[i].type != usb_redir_type_invalid) {
switch (dev->endpoint[i].type) {
case usb_redir_type_invalid:
break;
case usb_redir_type_iso:
case usb_redir_type_interrupt:
if (dev->endpoint[i].interval == 0) {
ERROR("Received 0 interval for isoc or irq endpoint\n");
usbredir_device_disconnect(dev);
}
/* Fall through */
case usb_redir_type_control:
case usb_redir_type_bulk:
DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
dev->endpoint[i].type, dev->endpoint[i].interface);
break;
default:
ERROR("Received invalid endpoint type\n");
usbredir_device_disconnect(dev);
}
}
}
@ -1029,7 +1117,7 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id,
DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
ep, id);
if (!dev->dev.attached) {
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
return;
}
@ -1050,7 +1138,7 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
DPRINTF("interrupt recv status %d ep %02X id %u\n",
interrupt_receiving_status->status, ep, id);
if (!dev->dev.attached) {
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
return;
}