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
This commit is contained in:
commit
9ca2140ab1
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
704
hw/usb-audio.c
Normal 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)
|
22
hw/usb-bt.c
22
hw/usb-bt.c
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
143
hw/usb-desc.c
143
hw/usb-desc.c
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
10
hw/usb-msd.c
10
hw/usb-msd.c
@ -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. */
|
||||
|
@ -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);
|
||||
|
14
hw/usb-net.c
14
hw/usb-net.c
@ -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: "
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
2749
hw/usb-xhci.c
Normal file
File diff suppressed because it is too large
Load Diff
125
hw/usb.c
125
hw/usb.c
@ -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;
|
||||
}
|
||||
|
44
hw/usb.h
44
hw/usb.h
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
452
usb-linux.c
452
usb-linux.c
@ -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,
|
||||
|
118
usb-redir.c
118
usb-redir.c
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user