@@ -245,6 +245,7 @@ sound-obj-$(CONFIG_AC97) += ac97.o
sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
sound-obj-$(CONFIG_CS4231A) += cs4231a.o
+sound-obj-$(CONFIG_USB_AUDIO) += usb-audio.o
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
@@ -430,7 +430,7 @@ struct soundhw soundhw[] = {
"pcspk",
"PC speaker",
0,
- 1,
+ BUS_ISA,
{ .init_isa = pcspk_audio_init }
},
#endif
@@ -440,7 +440,7 @@ struct soundhw soundhw[] = {
"sb16",
"Creative Sound Blaster 16",
0,
- 1,
+ BUS_ISA,
{ .init_isa = SB16_init }
},
#endif
@@ -450,7 +450,7 @@ struct soundhw soundhw[] = {
"cs4231a",
"CS4231A",
0,
- 1,
+ BUS_ISA,
{ .init_isa = cs4231a_init }
},
#endif
@@ -464,7 +464,7 @@ struct soundhw soundhw[] = {
"Yamaha YM3812 (OPL2)",
#endif
0,
- 1,
+ BUS_ISA,
{ .init_isa = Adlib_init }
},
#endif
@@ -474,7 +474,7 @@ struct soundhw soundhw[] = {
"gus",
"Gravis Ultrasound GF1",
0,
- 1,
+ BUS_ISA,
{ .init_isa = GUS_init }
},
#endif
@@ -484,7 +484,7 @@ struct soundhw soundhw[] = {
"ac97",
"Intel 82801AA AC97 Audio",
0,
- 0,
+ BUS_PCI,
{ .init_pci = ac97_init }
},
#endif
@@ -494,11 +494,21 @@ struct soundhw soundhw[] = {
"es1370",
"ENSONIQ AudioPCI ES1370",
0,
- 0,
+ BUS_PCI,
{ .init_pci = es1370_init }
},
#endif
+#ifdef CONFIG_USB_AUDIO
+ {
+ "usb",
+ "USB Audio",
+ 0,
+ BUS_USB,
+ { .init_usb = usb_audio_soundhw_init }
+ },
+#endif
+
#endif /* HAS_AUDIO_CHOICE */
{ NULL, NULL, 0, 0, { NULL } }
@@ -71,8 +71,8 @@ sparc_cpu=""
cross_prefix=""
cc="gcc"
audio_drv_list=""
-audio_card_list="ac97 es1370 sb16"
-audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus"
+audio_card_list="ac97 es1370 sb16 usb-audio"
+audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus usb-audio"
block_drv_whitelist=""
host_cc="gcc"
ar="ar"
@@ -2414,7 +2414,7 @@ if test "$vde" = "yes" ; then
fi
for card in $audio_card_list; do
def=CONFIG_`echo $card | tr '[:lower:]' '[:upper:]'`
- echo "$def=y" >> $config_host_mak
+ echo ${def//-/_}=y >> $config_host_mak
done
echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
for drv in $audio_drv_list; do
@@ -25,7 +25,7 @@ case $line in
CONFIG_AUDIO_DRIVERS=*)
drivers=${line#*=}
echo "#define CONFIG_AUDIO_DRIVERS \\"
- for drv in $drivers; do
+ for drv in ${drivers//-/_}; do
echo " &${drv}_audio_driver,\\"
done
echo ""
@@ -39,10 +39,12 @@ case $line in
;;
CONFIG_*=y) # configuration
name=${line%=*}
+ name=${name//-/_}
echo "#define $name 1"
;;
CONFIG_*=*) # configuration
name=${line%=*}
+ name=${name//-/_}
value=${line#*=}
echo "#define $name $value"
;;
@@ -15,3 +15,7 @@ int ac97_init(PCIBus *buf);
/* cs4231a.c */
int cs4231a_init(qemu_irq *pic);
+
+/* usb-audio.c */
+int usb_audio_soundhw_init(USBBus *bus);
+
@@ -756,13 +756,21 @@ void pc_audio_init (PCIBus *pci_bus, qemu_irq *pic)
for (c = soundhw; c->name; ++c) {
if (c->enabled) {
- if (c->isa) {
+ switch (c->bus) {
+ case BUS_ISA:
c->init.init_isa(pic);
- } else {
+ break;
+ case BUS_PCI:
if (pci_bus) {
c->init.init_pci(pci_bus);
}
- }
+ break;
+ case BUS_USB:
+ if (usb_enabled) {
+ c->init.init_usb(NULL /* FIXME */);
+ }
+ break;
+ }
}
}
}
@@ -167,8 +167,6 @@ static void pc_init1(ram_addr_t ram_size,
}
}
- pc_audio_init(pci_enabled ? pci_bus : NULL, isa_irq);
-
pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
idebus[0], idebus[1], floppy_controller, rtc_state);
@@ -176,6 +174,8 @@ static void pc_init1(ram_addr_t ram_size,
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
}
+ pc_audio_init(pci_enabled ? pci_bus : NULL, isa_irq);
+
if (pci_enabled && acpi_enabled) {
uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
i2c_bus *smbus;
new file mode 100644
@@ -0,0 +1,702 @@
+/*
+ * QEMU USB Net devices
+ *
+ * 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 "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+
+#define USBAUDIO_VENDOR_NUM 0xabcd
+#define USBAUDIO_PRODUCT_NUM 0x1234
+
+#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 char * const usb_audio_stringtable[] = {
+ [STRING_MANUFACTURER] = "QEMU",
+ [STRING_PRODUCT] = "QEMU USB Audio",
+ [STRING_SERIALNUMBER] = "1",
+ [STRING_CONFIG] = "QEMU USB Audio Configuration",
+ [STRING_USBAUDIO_CONTROL] = "QEMU USB Audio Device",
+ [STRING_INPUT_TERMINAL] = "QEMU USB Audio Output Pipe",
+ [STRING_FEATURE_UNIT] = "QEMU USB Audio Output Volume Control",
+ [STRING_OUTPUT_TERMINAL] = "QEMU USB Audio Output Terminal",
+ [STRING_NULL_STREAM] = "QEMU USB Audio Output - Disabled",
+ [STRING_REAL_STREAM] = "QEMU USB 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)
+
+static const uint8_t qemu_usb_audio_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ USB_DT_DEVICE, /* u8 bDescriptorType; Device */
+ 0x00, 0x02, /* u16 bcdUSB; v2.0 */
+ 0x00, /* u8 bDeviceClass; [ interface level ] */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full only ] */
+ 0x40, /* u8 bMaxPacketSize0 */
+ U16(USBAUDIO_VENDOR_NUM), /* u16 idVendor; */
+ U16(USBAUDIO_PRODUCT_NUM), /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+ STRING_MANUFACTURER, /* u8 iManufacturer; */
+ STRING_PRODUCT, /* u8 iProduct; */
+ STRING_SERIALNUMBER, /* u8 iSerialNumber; */
+ 0x01, /* u8 bNumConfigurations; */
+};
+
+/*
+ * A Basic Audio Device uses these specific values
+ */
+#define USBAUDIO_PACKET_SIZE 192
+#define USBAUDIO_SAMPLE_RATE 48000
+#define USBAUDIO_PACKET_INTERVAL 1
+
+/*
+ * This basically follows a "Basic Audio Device Headphone Type 1",
+ * except the Terminal Type is set to SPEAKER (0x0301) instead
+ * of HEADPHONE (0x0302)
+ */
+static const uint8_t qemu_usb_audio_config_descriptor[] = {
+ /* Configuration Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_CONFIG, /* u8 bDescriptorType */
+ U16(0x71), /* le16 wTotalLength */
+ 0x02, /* u8 bNumInterfaces */
+ DEV_CONFIG_VALUE, /* u8 bConfigurationValue */
+ STRING_CONFIG, /* u8 iConfiguration */
+ 0xc0, /* u8 bmAttributes */
+ 0x32, /* u8 bMaxPower */
+ /* USB Basic Headphone AC Interface */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x00, /* u8 bInterfaceNumber */
+ 0x00, /* u8 bAlternateSetting */
+ 0x00, /* u8 bNumEndpoints */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_CONTROL, /* u8 bInterfaceSubClass */
+ 0x04, /* u8 bInterfaceProtocol */
+ STRING_USBAUDIO_CONTROL, /* u8 iInterface */
+ /* Headphone Class-Specific AC Interface Header Descriptor */
+ 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 */
+ 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 */
+ 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 */
+ 0x09, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */
+ 0x03, /* u8 bUnitID */
+ U16(0x0301), /* u16 wTerminalType (SPEAKER) */
+ 0x00, /* u8 bAssocTerminal */
+ 0x02, /* u8 bSourceID */
+ STRING_OUTPUT_TERMINAL, /* u8 iTerminal */
+ /* Headphone Standard AS Interface Descriptor (Alt Set 0) */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x01, /* u8 bInterfaceNumber */
+ 0x00, /* u8 bAlternateSetting */
+ 0x00, /* u8 bNumEndpoints */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_STREAMING, /* u8 bInterfaceSubclass */
+ 0x00, /* u8 bInterfaceProtocol */
+ STRING_NULL_STREAM, /* u8 iInterface */
+ /* Headphone Standard AS Interface Descriptor (Alt Set 1) */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x01, /* u8 bInterfaceNumber */
+ 0x01, /* u8 bAlternateSetting */
+ 0x01, /* u8 bNumEndpoins */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_STREAMING, /* u8 bInterfaceSubclass */
+ 0x00, /* u8 bInterfaceProtocol */
+ STRING_REAL_STREAM, /* u8 iInterface */
+ /* Headphone Class-specific AS General Interface Descriptor */
+ 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 */
+ 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 */
+ /* Stereo Headphone Standard AS Audio Data Endpoint Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_ENDPOINT, /* u8 bDescriptorType */
+ 0x01, /* u8 bEndpointAddress */
+ 0x0d, /* u8 bmAttributes */
+ U16(USBAUDIO_PACKET_SIZE), /* u16 wMaxPacketSize */
+ USBAUDIO_PACKET_INTERVAL, /* u8 bInterval */
+ 0x00, /* u8 bRefresh */
+ 0x00, /* u8 bSynchAddress */
+ /* Stereo Headphone Class-specific AS Audio Data Endpoint Descriptor */
+ 0x07, /* u8 bLength */
+ USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */
+ DST_EP_GENERAL, /* u8 bDescriptorSubtype */
+ 0x00, /* u8 bmAttributes */
+ 0x00, /* u8 bLockDelayUnits */
+ U16(0x0000), /* u16 wLockDelay */
+};
+
+/*
+ * 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
+
+typedef struct USBAudioState {
+ USBDevice dev;
+ QEMUSoundCard card;
+ enum usb_audio_altset outaltset;
+ SWVoiceOut *outvoice;
+ bool mute;
+ uint8_t vol[2];
+} USBAudioState;
+
+static void output_callback(void *opaque, int avail)
+{
+ (void)opaque;
+ (void)avail;
+}
+
+static int usb_audio_set_output_altset(USBAudioState *s, int altset)
+{
+ struct audsettings as;
+
+ if (altset == s->outaltset)
+ return 0;
+
+ switch (altset) {
+ case ALTSET_OFF:
+ as.nchannels = 0;
+ break;
+
+ case ALTSET_ON:
+ as.freq = USBAUDIO_SAMPLE_RATE;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (s->outvoice) {
+ AUD_close_out (&s->card, s->outvoice);
+ s->outvoice = NULL;
+ }
+
+ if (as.nchannels) {
+ s->outvoice = AUD_open_out (&s->card, s->outvoice, "usb-audio",
+ s, output_callback, &as);
+ AUD_set_volume_out (s->outvoice, s->mute, s->vol[0], s->vol[1]);
+ AUD_set_active_out (s->outvoice, 1);
+ }
+
+ fprintf(stderr, "usb-audio: set interface %d\n", altset);
+
+ s->outaltset = 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->mute;
+ ret = 1;
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
+ if (cn < 2) {
+ uint16_t vol = (s->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->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);
+
+ fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
+
+ vol -= 0x8000;
+ vol = (vol * 255 + 0x4400) / 0x8800;
+ if (vol > 255)
+ vol = 255;
+
+ s->vol[cn] = vol;
+ set_vol = true;
+ ret = 0;
+ }
+ break;
+ }
+
+ if (set_vol) {
+ fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
+ s->mute, s->vol[0], s->vol[1]);
+ AUD_set_volume_out (s->outvoice, s->mute, s->vol[0], s->vol[1]);
+ }
+
+ return ret;
+}
+
+static int usb_audio_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ int ret = 0;
+
+ fprintf(stderr, "usb-audio: control transaction: "
+ "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+ request, value, index, length);
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ ret = sizeof(qemu_usb_audio_dev_descriptor);
+ memcpy(data, qemu_usb_audio_dev_descriptor, ret);
+ break;
+
+ case USB_DT_CONFIG:
+ switch (value & 0xff) {
+ case 0:
+ ret = sizeof(qemu_usb_audio_config_descriptor);
+ memcpy(data, qemu_usb_audio_config_descriptor, ret);
+ break;
+
+ default:
+ goto fail;
+ }
+
+ data[2] = ret & 0xff;
+ data[3] = ret >> 8;
+ break;
+
+ case USB_DT_STRING:
+ switch (value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+
+ default:
+ if (usb_audio_stringtable[value & 0xff]) {
+ ret = set_usb_string(data,
+ usb_audio_stringtable[value & 0xff]);
+ break;
+ }
+
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = DEV_CONFIG_VALUE;
+ ret = 1;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ switch (value & 0xff) {
+ case DEV_CONFIG_VALUE:
+ break;
+
+ default:
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ if (index != 0x01)
+ goto fail;
+ data[0] = s->outaltset;
+ ret = 1;
+ break;
+
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ if (index != 0x01)
+ goto fail;
+ if (usb_audio_set_output_altset(s, value))
+ goto fail;
+
+ ret = 0;
+ break;
+
+ 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)
+ 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)
+ goto fail;
+ break;
+
+ default:
+ fail:
+ 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_handle_reset(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ 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 rv;
+
+ /*
+ * Note: allow for the case where the USB HCI model's notion of time
+ * is faster than the audio device, which means we'll eventually overrun
+ * the output buffer. Drop those samples, sorry. Can we do better?
+ */
+ rv = AUD_write (s->outvoice, p->data, p->len);
+
+ if (rv < p->len)
+ fprintf(stderr, "usb-audio: dropped %d samples\n", (p->len - rv) >> 2);
+
+ 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)
+ fprintf(stderr, "usb-audio: failed data transaction: "
+ "pid 0x%x ep 0x%x len 0x%x\n",
+ p->pid, p->devep, p->len);
+ return ret;
+}
+
+static void usb_audio_handle_destroy(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ fprintf(stderr, "usb-audio: destroy\n");
+
+ usb_audio_set_output_altset(s, ALTSET_OFF);
+}
+
+static int usb_audio_initfn(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->outvoice = NULL;
+ s->outaltset = ALTSET_OFF;
+ s->mute = false;
+ s->vol[0] = 240; /* 0 dB */
+ s->vol[1] = 240; /* 0 dB */
+ return 0;
+}
+
+static USBDevice *usb_audio_init(USBBus *bus)
+{
+ USBDevice *dev;
+ USBAudioState *s;
+
+ dev = usb_create_simple(bus, "usb-audio");
+ s = DO_UPCAST(struct USBAudioState, dev, dev);
+ s->dev.opaque = s;
+
+ AUD_register_card("usb-audio", &s->card);
+
+ return dev;
+}
+
+/* Called by "-soundhw usb" */
+int usb_audio_soundhw_init(USBBus *bus)
+{
+ return usb_audio_init(bus) ? 0 : -1;
+}
+
+/* Called by "-usb audio" */
+static USBDevice *usb_audio_usb_init(const char *cmdline)
+{
+ USBDevice *dev = usb_audio_init(NULL /* FIXME */);
+
+ return dev;
+}
+
+static struct USBDeviceInfo usb_audio_info = {
+ .product_desc = "QEMU USB Audio Interface",
+ .qdev.name = "usb-audio",
+ .qdev.size = sizeof(USBAudioState),
+ .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,
+ .usbdevice_name = "audio",
+ .usbdevice_init = usb_audio_usb_init,
+};
+
+static void usb_audio_register_devices(void)
+{
+ usb_qdev_register(&usb_audio_info);
+}
+
+device_init (usb_audio_register_devices)
@@ -68,9 +68,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
@@ -67,6 +67,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
@@ -116,18 +121,14 @@
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
+#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
-typedef struct USBBus USBBus;
-typedef struct USBPort USBPort;
-typedef struct USBDevice USBDevice;
-typedef struct USBDeviceInfo USBDeviceInfo;
-typedef struct USBPacket USBPacket;
-
/* definition of a USB device */
struct USBDevice {
DeviceState qdev;
@@ -229,6 +229,11 @@ typedef struct I2SCodec I2SCodec;
typedef struct SSIBus SSIBus;
typedef struct EventNotifier EventNotifier;
typedef struct VirtIODevice VirtIODevice;
+typedef struct USBBus USBBus;
+typedef struct USBPort USBPort;
+typedef struct USBDevice USBDevice;
+typedef struct USBDeviceInfo USBDeviceInfo;
+typedef struct USBPacket USBPacket;
typedef uint64_t pcibus_t;
@@ -172,14 +172,21 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
#ifdef HAS_AUDIO
+enum bus_type {
+ BUS_PCI,
+ BUS_ISA,
+ BUS_USB,
+};
+
struct soundhw {
const char *name;
const char *descr;
int enabled;
- int isa;
+ enum bus_type bus;
union {
- int (*init_isa) (qemu_irq *pic);
int (*init_pci) (PCIBus *bus);
+ int (*init_isa) (qemu_irq *pic);
+ int (*init_usb) (USBBus *bus);
} init;
};