1f1ae32a1SGerd Hoffmann /* 2f1ae32a1SGerd Hoffmann * QEMU USB audio device 3f1ae32a1SGerd Hoffmann * 4f1ae32a1SGerd Hoffmann * written by: 5f1ae32a1SGerd Hoffmann * H. Peter Anvin <hpa@linux.intel.com> 6f1ae32a1SGerd Hoffmann * Gerd Hoffmann <kraxel@redhat.com> 7f1ae32a1SGerd Hoffmann * 8f1ae32a1SGerd Hoffmann * lousely based on usb net device code which is: 9f1ae32a1SGerd Hoffmann * 10f1ae32a1SGerd Hoffmann * Copyright (c) 2006 Thomas Sailer 11f1ae32a1SGerd Hoffmann * Copyright (c) 2008 Andrzej Zaborowski 12f1ae32a1SGerd Hoffmann * 13f1ae32a1SGerd Hoffmann * Permission is hereby granted, free of charge, to any person obtaining a copy 14f1ae32a1SGerd Hoffmann * of this software and associated documentation files (the "Software"), to deal 15f1ae32a1SGerd Hoffmann * in the Software without restriction, including without limitation the rights 16f1ae32a1SGerd Hoffmann * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17f1ae32a1SGerd Hoffmann * copies of the Software, and to permit persons to whom the Software is 18f1ae32a1SGerd Hoffmann * furnished to do so, subject to the following conditions: 19f1ae32a1SGerd Hoffmann * 20f1ae32a1SGerd Hoffmann * The above copyright notice and this permission notice shall be included in 21f1ae32a1SGerd Hoffmann * all copies or substantial portions of the Software. 22f1ae32a1SGerd Hoffmann * 23f1ae32a1SGerd Hoffmann * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24f1ae32a1SGerd Hoffmann * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25f1ae32a1SGerd Hoffmann * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 26f1ae32a1SGerd Hoffmann * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27f1ae32a1SGerd Hoffmann * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28f1ae32a1SGerd Hoffmann * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29f1ae32a1SGerd Hoffmann * THE SOFTWARE. 30f1ae32a1SGerd Hoffmann */ 31f1ae32a1SGerd Hoffmann 32f1ae32a1SGerd Hoffmann #include "qemu-common.h" 33f1ae32a1SGerd Hoffmann #include "hw/usb.h" 34f1ae32a1SGerd Hoffmann #include "hw/usb/desc.h" 35f1ae32a1SGerd Hoffmann #include "hw/hw.h" 36f1ae32a1SGerd Hoffmann #include "audio/audio.h" 37f1ae32a1SGerd Hoffmann 38f1ae32a1SGerd Hoffmann #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ 39f1ae32a1SGerd Hoffmann #define USBAUDIO_PRODUCT_NUM 0x0002 40f1ae32a1SGerd Hoffmann 41f1ae32a1SGerd Hoffmann #define DEV_CONFIG_VALUE 1 /* The one and only */ 42f1ae32a1SGerd Hoffmann 43f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AC interfaces */ 44f1ae32a1SGerd Hoffmann #define DST_AC_HEADER 1 45f1ae32a1SGerd Hoffmann #define DST_AC_INPUT_TERMINAL 2 46f1ae32a1SGerd Hoffmann #define DST_AC_OUTPUT_TERMINAL 3 47f1ae32a1SGerd Hoffmann #define DST_AC_FEATURE_UNIT 6 48f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AS interfaces */ 49f1ae32a1SGerd Hoffmann #define DST_AS_GENERAL 1 50f1ae32a1SGerd Hoffmann #define DST_AS_FORMAT_TYPE 2 51f1ae32a1SGerd Hoffmann /* Descriptor subtypes for endpoints */ 52f1ae32a1SGerd Hoffmann #define DST_EP_GENERAL 1 53f1ae32a1SGerd Hoffmann 54f1ae32a1SGerd Hoffmann enum usb_audio_strings { 55f1ae32a1SGerd Hoffmann STRING_NULL, 56f1ae32a1SGerd Hoffmann STRING_MANUFACTURER, 57f1ae32a1SGerd Hoffmann STRING_PRODUCT, 58f1ae32a1SGerd Hoffmann STRING_SERIALNUMBER, 59f1ae32a1SGerd Hoffmann STRING_CONFIG, 60f1ae32a1SGerd Hoffmann STRING_USBAUDIO_CONTROL, 61f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, 62f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, 63f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, 64f1ae32a1SGerd Hoffmann STRING_NULL_STREAM, 65f1ae32a1SGerd Hoffmann STRING_REAL_STREAM, 66f1ae32a1SGerd Hoffmann }; 67f1ae32a1SGerd Hoffmann 68f1ae32a1SGerd Hoffmann static const USBDescStrings usb_audio_stringtable = { 69f1ae32a1SGerd Hoffmann [STRING_MANUFACTURER] = "QEMU", 70f1ae32a1SGerd Hoffmann [STRING_PRODUCT] = "QEMU USB Audio", 71f1ae32a1SGerd Hoffmann [STRING_SERIALNUMBER] = "1", 72f1ae32a1SGerd Hoffmann [STRING_CONFIG] = "Audio Configuration", 73f1ae32a1SGerd Hoffmann [STRING_USBAUDIO_CONTROL] = "Audio Device", 74f1ae32a1SGerd Hoffmann [STRING_INPUT_TERMINAL] = "Audio Output Pipe", 75f1ae32a1SGerd Hoffmann [STRING_FEATURE_UNIT] = "Audio Output Volume Control", 76f1ae32a1SGerd Hoffmann [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", 77f1ae32a1SGerd Hoffmann [STRING_NULL_STREAM] = "Audio Output - Disabled", 78f1ae32a1SGerd Hoffmann [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", 79f1ae32a1SGerd Hoffmann }; 80f1ae32a1SGerd Hoffmann 81f1ae32a1SGerd Hoffmann #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) 82f1ae32a1SGerd Hoffmann #define U24(x) U16(x), (((x) >> 16) & 0xff) 83f1ae32a1SGerd Hoffmann #define U32(x) U24(x), (((x) >> 24) & 0xff) 84f1ae32a1SGerd Hoffmann 85f1ae32a1SGerd Hoffmann /* 86f1ae32a1SGerd Hoffmann * A Basic Audio Device uses these specific values 87f1ae32a1SGerd Hoffmann */ 88f1ae32a1SGerd Hoffmann #define USBAUDIO_PACKET_SIZE 192 89f1ae32a1SGerd Hoffmann #define USBAUDIO_SAMPLE_RATE 48000 90f1ae32a1SGerd Hoffmann #define USBAUDIO_PACKET_INTERVAL 1 91f1ae32a1SGerd Hoffmann 92f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface[] = { 93f1ae32a1SGerd Hoffmann { 94f1ae32a1SGerd Hoffmann .bInterfaceNumber = 0, 95f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 96f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 97f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, 98f1ae32a1SGerd Hoffmann .bInterfaceProtocol = 0x04, 99f1ae32a1SGerd Hoffmann .iInterface = STRING_USBAUDIO_CONTROL, 100f1ae32a1SGerd Hoffmann .ndesc = 4, 101f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 102f1ae32a1SGerd Hoffmann { 103f1ae32a1SGerd Hoffmann /* Headphone Class-Specific AC Interface Header Descriptor */ 104f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 105f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 106f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 107f1ae32a1SGerd Hoffmann DST_AC_HEADER, /* u8 bDescriptorSubtype */ 108f1ae32a1SGerd Hoffmann U16(0x0100), /* u16 bcdADC */ 109f1ae32a1SGerd Hoffmann U16(0x2b), /* u16 wTotalLength */ 110f1ae32a1SGerd Hoffmann 0x01, /* u8 bInCollection */ 111f1ae32a1SGerd Hoffmann 0x01, /* u8 baInterfaceNr */ 112f1ae32a1SGerd Hoffmann } 113f1ae32a1SGerd Hoffmann },{ 114f1ae32a1SGerd Hoffmann /* Generic Stereo Input Terminal ID1 Descriptor */ 115f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 116f1ae32a1SGerd Hoffmann 0x0c, /* u8 bLength */ 117f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 118f1ae32a1SGerd Hoffmann DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ 119f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalID */ 120f1ae32a1SGerd Hoffmann U16(0x0101), /* u16 wTerminalType */ 121f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 122f1ae32a1SGerd Hoffmann 0x02, /* u16 bNrChannels */ 123f1ae32a1SGerd Hoffmann U16(0x0003), /* u16 wChannelConfig */ 124f1ae32a1SGerd Hoffmann 0x00, /* u8 iChannelNames */ 125f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, /* u8 iTerminal */ 126f1ae32a1SGerd Hoffmann } 127f1ae32a1SGerd Hoffmann },{ 128f1ae32a1SGerd Hoffmann /* Generic Stereo Feature Unit ID2 Descriptor */ 129f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 130f1ae32a1SGerd Hoffmann 0x0d, /* u8 bLength */ 131f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 132f1ae32a1SGerd Hoffmann DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ 133f1ae32a1SGerd Hoffmann 0x02, /* u8 bUnitID */ 134f1ae32a1SGerd Hoffmann 0x01, /* u8 bSourceID */ 135f1ae32a1SGerd Hoffmann 0x02, /* u8 bControlSize */ 136f1ae32a1SGerd Hoffmann U16(0x0001), /* u16 bmaControls(0) */ 137f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(1) */ 138f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(2) */ 139f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, /* u8 iFeature */ 140f1ae32a1SGerd Hoffmann } 141f1ae32a1SGerd Hoffmann },{ 142f1ae32a1SGerd Hoffmann /* Headphone Ouptut Terminal ID3 Descriptor */ 143f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 144f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 145f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 146f1ae32a1SGerd Hoffmann DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ 147f1ae32a1SGerd Hoffmann 0x03, /* u8 bUnitID */ 148f1ae32a1SGerd Hoffmann U16(0x0301), /* u16 wTerminalType (SPK) */ 149f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 150f1ae32a1SGerd Hoffmann 0x02, /* u8 bSourceID */ 151f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ 152f1ae32a1SGerd Hoffmann } 153f1ae32a1SGerd Hoffmann } 154f1ae32a1SGerd Hoffmann }, 155f1ae32a1SGerd Hoffmann },{ 156f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 157f1ae32a1SGerd Hoffmann .bAlternateSetting = 0, 158f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 159f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 160f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 161f1ae32a1SGerd Hoffmann .iInterface = STRING_NULL_STREAM, 162f1ae32a1SGerd Hoffmann },{ 163f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 164f1ae32a1SGerd Hoffmann .bAlternateSetting = 1, 165f1ae32a1SGerd Hoffmann .bNumEndpoints = 1, 166f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 167f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 168f1ae32a1SGerd Hoffmann .iInterface = STRING_REAL_STREAM, 169f1ae32a1SGerd Hoffmann .ndesc = 2, 170f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 171f1ae32a1SGerd Hoffmann { 172f1ae32a1SGerd Hoffmann /* Headphone Class-specific AS General Interface Descriptor */ 173f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 174f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 175f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 176f1ae32a1SGerd Hoffmann DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 177f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalLink */ 178f1ae32a1SGerd Hoffmann 0x00, /* u8 bDelay */ 179f1ae32a1SGerd Hoffmann 0x01, 0x00, /* u16 wFormatTag */ 180f1ae32a1SGerd Hoffmann } 181f1ae32a1SGerd Hoffmann },{ 182f1ae32a1SGerd Hoffmann /* Headphone Type I Format Type Descriptor */ 183f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 184f1ae32a1SGerd Hoffmann 0x0b, /* u8 bLength */ 185f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 186f1ae32a1SGerd Hoffmann DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 187f1ae32a1SGerd Hoffmann 0x01, /* u8 bFormatType */ 188f1ae32a1SGerd Hoffmann 0x02, /* u8 bNrChannels */ 189f1ae32a1SGerd Hoffmann 0x02, /* u8 bSubFrameSize */ 190f1ae32a1SGerd Hoffmann 0x10, /* u8 bBitResolution */ 191f1ae32a1SGerd Hoffmann 0x01, /* u8 bSamFreqType */ 192f1ae32a1SGerd Hoffmann U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 193f1ae32a1SGerd Hoffmann } 194f1ae32a1SGerd Hoffmann } 195f1ae32a1SGerd Hoffmann }, 196f1ae32a1SGerd Hoffmann .eps = (USBDescEndpoint[]) { 197f1ae32a1SGerd Hoffmann { 198f1ae32a1SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | 0x01, 199f1ae32a1SGerd Hoffmann .bmAttributes = 0x0d, 200f1ae32a1SGerd Hoffmann .wMaxPacketSize = USBAUDIO_PACKET_SIZE, 201f1ae32a1SGerd Hoffmann .bInterval = 1, 202f1ae32a1SGerd Hoffmann .is_audio = 1, 203f1ae32a1SGerd Hoffmann /* Stereo Headphone Class-specific 204f1ae32a1SGerd Hoffmann AS Audio Data Endpoint Descriptor */ 205f1ae32a1SGerd Hoffmann .extra = (uint8_t[]) { 206f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 207f1ae32a1SGerd Hoffmann USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 208f1ae32a1SGerd Hoffmann DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 209f1ae32a1SGerd Hoffmann 0x00, /* u8 bmAttributes */ 210f1ae32a1SGerd Hoffmann 0x00, /* u8 bLockDelayUnits */ 211f1ae32a1SGerd Hoffmann U16(0x0000), /* u16 wLockDelay */ 212f1ae32a1SGerd Hoffmann }, 213f1ae32a1SGerd Hoffmann }, 214f1ae32a1SGerd Hoffmann } 215f1ae32a1SGerd Hoffmann } 216f1ae32a1SGerd Hoffmann }; 217f1ae32a1SGerd Hoffmann 218f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device = { 2192bbd086cSGerd Hoffmann .bcdUSB = 0x0100, 220f1ae32a1SGerd Hoffmann .bMaxPacketSize0 = 64, 221f1ae32a1SGerd Hoffmann .bNumConfigurations = 1, 222f1ae32a1SGerd Hoffmann .confs = (USBDescConfig[]) { 223f1ae32a1SGerd Hoffmann { 224f1ae32a1SGerd Hoffmann .bNumInterfaces = 2, 225f1ae32a1SGerd Hoffmann .bConfigurationValue = DEV_CONFIG_VALUE, 226f1ae32a1SGerd Hoffmann .iConfiguration = STRING_CONFIG, 227*bd93976aSPantelis Koukousoulas .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, 228f1ae32a1SGerd Hoffmann .bMaxPower = 0x32, 229f1ae32a1SGerd Hoffmann .nif = ARRAY_SIZE(desc_iface), 230f1ae32a1SGerd Hoffmann .ifs = desc_iface, 231f1ae32a1SGerd Hoffmann }, 232f1ae32a1SGerd Hoffmann }, 233f1ae32a1SGerd Hoffmann }; 234f1ae32a1SGerd Hoffmann 235f1ae32a1SGerd Hoffmann static const USBDesc desc_audio = { 236f1ae32a1SGerd Hoffmann .id = { 237f1ae32a1SGerd Hoffmann .idVendor = USBAUDIO_VENDOR_NUM, 238f1ae32a1SGerd Hoffmann .idProduct = USBAUDIO_PRODUCT_NUM, 239f1ae32a1SGerd Hoffmann .bcdDevice = 0, 240f1ae32a1SGerd Hoffmann .iManufacturer = STRING_MANUFACTURER, 241f1ae32a1SGerd Hoffmann .iProduct = STRING_PRODUCT, 242f1ae32a1SGerd Hoffmann .iSerialNumber = STRING_SERIALNUMBER, 243f1ae32a1SGerd Hoffmann }, 244f1ae32a1SGerd Hoffmann .full = &desc_device, 245f1ae32a1SGerd Hoffmann .str = usb_audio_stringtable, 246f1ae32a1SGerd Hoffmann }; 247f1ae32a1SGerd Hoffmann 248f1ae32a1SGerd Hoffmann /* 249f1ae32a1SGerd Hoffmann * A USB audio device supports an arbitrary number of alternate 250f1ae32a1SGerd Hoffmann * interface settings for each interface. Each corresponds to a block 251f1ae32a1SGerd Hoffmann * diagram of parameterized blocks. This can thus refer to things like 252f1ae32a1SGerd Hoffmann * number of channels, data rates, or in fact completely different 253f1ae32a1SGerd Hoffmann * block diagrams. Alternative setting 0 is always the null block diagram, 254f1ae32a1SGerd Hoffmann * which is used by a disabled device. 255f1ae32a1SGerd Hoffmann */ 256f1ae32a1SGerd Hoffmann enum usb_audio_altset { 257f1ae32a1SGerd Hoffmann ALTSET_OFF = 0x00, /* No endpoint */ 258f1ae32a1SGerd Hoffmann ALTSET_ON = 0x01, /* Single endpoint */ 259f1ae32a1SGerd Hoffmann }; 260f1ae32a1SGerd Hoffmann 261f1ae32a1SGerd Hoffmann /* 262f1ae32a1SGerd Hoffmann * Class-specific control requests 263f1ae32a1SGerd Hoffmann */ 264f1ae32a1SGerd Hoffmann #define CR_SET_CUR 0x01 265f1ae32a1SGerd Hoffmann #define CR_GET_CUR 0x81 266f1ae32a1SGerd Hoffmann #define CR_SET_MIN 0x02 267f1ae32a1SGerd Hoffmann #define CR_GET_MIN 0x82 268f1ae32a1SGerd Hoffmann #define CR_SET_MAX 0x03 269f1ae32a1SGerd Hoffmann #define CR_GET_MAX 0x83 270f1ae32a1SGerd Hoffmann #define CR_SET_RES 0x04 271f1ae32a1SGerd Hoffmann #define CR_GET_RES 0x84 272f1ae32a1SGerd Hoffmann #define CR_SET_MEM 0x05 273f1ae32a1SGerd Hoffmann #define CR_GET_MEM 0x85 274f1ae32a1SGerd Hoffmann #define CR_GET_STAT 0xff 275f1ae32a1SGerd Hoffmann 276f1ae32a1SGerd Hoffmann /* 277f1ae32a1SGerd Hoffmann * Feature Unit Control Selectors 278f1ae32a1SGerd Hoffmann */ 279f1ae32a1SGerd Hoffmann #define MUTE_CONTROL 0x01 280f1ae32a1SGerd Hoffmann #define VOLUME_CONTROL 0x02 281f1ae32a1SGerd Hoffmann #define BASS_CONTROL 0x03 282f1ae32a1SGerd Hoffmann #define MID_CONTROL 0x04 283f1ae32a1SGerd Hoffmann #define TREBLE_CONTROL 0x05 284f1ae32a1SGerd Hoffmann #define GRAPHIC_EQUALIZER_CONTROL 0x06 285f1ae32a1SGerd Hoffmann #define AUTOMATIC_GAIN_CONTROL 0x07 286f1ae32a1SGerd Hoffmann #define DELAY_CONTROL 0x08 287f1ae32a1SGerd Hoffmann #define BASS_BOOST_CONTROL 0x09 288f1ae32a1SGerd Hoffmann #define LOUDNESS_CONTROL 0x0a 289f1ae32a1SGerd Hoffmann 290f1ae32a1SGerd Hoffmann /* 291f1ae32a1SGerd Hoffmann * buffering 292f1ae32a1SGerd Hoffmann */ 293f1ae32a1SGerd Hoffmann 294f1ae32a1SGerd Hoffmann struct streambuf { 295f1ae32a1SGerd Hoffmann uint8_t *data; 296f1ae32a1SGerd Hoffmann uint32_t size; 297f1ae32a1SGerd Hoffmann uint32_t prod; 298f1ae32a1SGerd Hoffmann uint32_t cons; 299f1ae32a1SGerd Hoffmann }; 300f1ae32a1SGerd Hoffmann 301f1ae32a1SGerd Hoffmann static void streambuf_init(struct streambuf *buf, uint32_t size) 302f1ae32a1SGerd Hoffmann { 303f1ae32a1SGerd Hoffmann g_free(buf->data); 304f1ae32a1SGerd Hoffmann buf->size = size - (size % USBAUDIO_PACKET_SIZE); 305f1ae32a1SGerd Hoffmann buf->data = g_malloc(buf->size); 306f1ae32a1SGerd Hoffmann buf->prod = 0; 307f1ae32a1SGerd Hoffmann buf->cons = 0; 308f1ae32a1SGerd Hoffmann } 309f1ae32a1SGerd Hoffmann 310f1ae32a1SGerd Hoffmann static void streambuf_fini(struct streambuf *buf) 311f1ae32a1SGerd Hoffmann { 312f1ae32a1SGerd Hoffmann g_free(buf->data); 313f1ae32a1SGerd Hoffmann buf->data = NULL; 314f1ae32a1SGerd Hoffmann } 315f1ae32a1SGerd Hoffmann 316f1ae32a1SGerd Hoffmann static int streambuf_put(struct streambuf *buf, USBPacket *p) 317f1ae32a1SGerd Hoffmann { 318f1ae32a1SGerd Hoffmann uint32_t free = buf->size - (buf->prod - buf->cons); 319f1ae32a1SGerd Hoffmann 320f1ae32a1SGerd Hoffmann if (!free) { 321f1ae32a1SGerd Hoffmann return 0; 322f1ae32a1SGerd Hoffmann } 323f1ae32a1SGerd Hoffmann assert(free >= USBAUDIO_PACKET_SIZE); 324f1ae32a1SGerd Hoffmann usb_packet_copy(p, buf->data + (buf->prod % buf->size), 325f1ae32a1SGerd Hoffmann USBAUDIO_PACKET_SIZE); 326f1ae32a1SGerd Hoffmann buf->prod += USBAUDIO_PACKET_SIZE; 327f1ae32a1SGerd Hoffmann return USBAUDIO_PACKET_SIZE; 328f1ae32a1SGerd Hoffmann } 329f1ae32a1SGerd Hoffmann 330f1ae32a1SGerd Hoffmann static uint8_t *streambuf_get(struct streambuf *buf) 331f1ae32a1SGerd Hoffmann { 332f1ae32a1SGerd Hoffmann uint32_t used = buf->prod - buf->cons; 333f1ae32a1SGerd Hoffmann uint8_t *data; 334f1ae32a1SGerd Hoffmann 335f1ae32a1SGerd Hoffmann if (!used) { 336f1ae32a1SGerd Hoffmann return NULL; 337f1ae32a1SGerd Hoffmann } 338f1ae32a1SGerd Hoffmann assert(used >= USBAUDIO_PACKET_SIZE); 339f1ae32a1SGerd Hoffmann data = buf->data + (buf->cons % buf->size); 340f1ae32a1SGerd Hoffmann buf->cons += USBAUDIO_PACKET_SIZE; 341f1ae32a1SGerd Hoffmann return data; 342f1ae32a1SGerd Hoffmann } 343f1ae32a1SGerd Hoffmann 344f1ae32a1SGerd Hoffmann typedef struct USBAudioState { 345f1ae32a1SGerd Hoffmann /* qemu interfaces */ 346f1ae32a1SGerd Hoffmann USBDevice dev; 347f1ae32a1SGerd Hoffmann QEMUSoundCard card; 348f1ae32a1SGerd Hoffmann 349f1ae32a1SGerd Hoffmann /* state */ 350f1ae32a1SGerd Hoffmann struct { 351f1ae32a1SGerd Hoffmann enum usb_audio_altset altset; 352f1ae32a1SGerd Hoffmann struct audsettings as; 353f1ae32a1SGerd Hoffmann SWVoiceOut *voice; 354f1ae32a1SGerd Hoffmann bool mute; 355f1ae32a1SGerd Hoffmann uint8_t vol[2]; 356f1ae32a1SGerd Hoffmann struct streambuf buf; 357f1ae32a1SGerd Hoffmann } out; 358f1ae32a1SGerd Hoffmann 359f1ae32a1SGerd Hoffmann /* properties */ 360f1ae32a1SGerd Hoffmann uint32_t debug; 361f1ae32a1SGerd Hoffmann uint32_t buffer; 362f1ae32a1SGerd Hoffmann } USBAudioState; 363f1ae32a1SGerd Hoffmann 364f1ae32a1SGerd Hoffmann static void output_callback(void *opaque, int avail) 365f1ae32a1SGerd Hoffmann { 366f1ae32a1SGerd Hoffmann USBAudioState *s = opaque; 367f1ae32a1SGerd Hoffmann uint8_t *data; 368f1ae32a1SGerd Hoffmann 369f1ae32a1SGerd Hoffmann for (;;) { 370f1ae32a1SGerd Hoffmann if (avail < USBAUDIO_PACKET_SIZE) { 371f1ae32a1SGerd Hoffmann return; 372f1ae32a1SGerd Hoffmann } 373f1ae32a1SGerd Hoffmann data = streambuf_get(&s->out.buf); 374f1ae32a1SGerd Hoffmann if (NULL == data) { 375f1ae32a1SGerd Hoffmann return; 376f1ae32a1SGerd Hoffmann } 377f1ae32a1SGerd Hoffmann AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE); 378f1ae32a1SGerd Hoffmann avail -= USBAUDIO_PACKET_SIZE; 379f1ae32a1SGerd Hoffmann } 380f1ae32a1SGerd Hoffmann } 381f1ae32a1SGerd Hoffmann 382f1ae32a1SGerd Hoffmann static int usb_audio_set_output_altset(USBAudioState *s, int altset) 383f1ae32a1SGerd Hoffmann { 384f1ae32a1SGerd Hoffmann switch (altset) { 385f1ae32a1SGerd Hoffmann case ALTSET_OFF: 386f1ae32a1SGerd Hoffmann streambuf_init(&s->out.buf, s->buffer); 387f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, false); 388f1ae32a1SGerd Hoffmann break; 389f1ae32a1SGerd Hoffmann case ALTSET_ON: 390f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, true); 391f1ae32a1SGerd Hoffmann break; 392f1ae32a1SGerd Hoffmann default: 393f1ae32a1SGerd Hoffmann return -1; 394f1ae32a1SGerd Hoffmann } 395f1ae32a1SGerd Hoffmann 396f1ae32a1SGerd Hoffmann if (s->debug) { 397f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: set interface %d\n", altset); 398f1ae32a1SGerd Hoffmann } 399f1ae32a1SGerd Hoffmann s->out.altset = altset; 400f1ae32a1SGerd Hoffmann return 0; 401f1ae32a1SGerd Hoffmann } 402f1ae32a1SGerd Hoffmann 403f1ae32a1SGerd Hoffmann /* 404f1ae32a1SGerd Hoffmann * Note: we arbitrarily map the volume control range onto -inf..+8 dB 405f1ae32a1SGerd Hoffmann */ 406f1ae32a1SGerd Hoffmann #define ATTRIB_ID(cs, attrib, idif) \ 407f1ae32a1SGerd Hoffmann (((cs) << 24) | ((attrib) << 16) | (idif)) 408f1ae32a1SGerd Hoffmann 409f1ae32a1SGerd Hoffmann static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, 410f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 411f1ae32a1SGerd Hoffmann int length, uint8_t *data) 412f1ae32a1SGerd Hoffmann { 413f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 414f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 415f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 416f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 417f1ae32a1SGerd Hoffmann 418f1ae32a1SGerd Hoffmann switch (aid) { 419f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): 420f1ae32a1SGerd Hoffmann data[0] = s->out.mute; 421f1ae32a1SGerd Hoffmann ret = 1; 422f1ae32a1SGerd Hoffmann break; 423f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): 424f1ae32a1SGerd Hoffmann if (cn < 2) { 425f1ae32a1SGerd Hoffmann uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000; 426f1ae32a1SGerd Hoffmann data[0] = vol; 427f1ae32a1SGerd Hoffmann data[1] = vol >> 8; 428f1ae32a1SGerd Hoffmann ret = 2; 429f1ae32a1SGerd Hoffmann } 430f1ae32a1SGerd Hoffmann break; 431f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): 432f1ae32a1SGerd Hoffmann if (cn < 2) { 433f1ae32a1SGerd Hoffmann data[0] = 0x01; 434f1ae32a1SGerd Hoffmann data[1] = 0x80; 435f1ae32a1SGerd Hoffmann ret = 2; 436f1ae32a1SGerd Hoffmann } 437f1ae32a1SGerd Hoffmann break; 438f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): 439f1ae32a1SGerd Hoffmann if (cn < 2) { 440f1ae32a1SGerd Hoffmann data[0] = 0x00; 441f1ae32a1SGerd Hoffmann data[1] = 0x08; 442f1ae32a1SGerd Hoffmann ret = 2; 443f1ae32a1SGerd Hoffmann } 444f1ae32a1SGerd Hoffmann break; 445f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): 446f1ae32a1SGerd Hoffmann if (cn < 2) { 447f1ae32a1SGerd Hoffmann data[0] = 0x88; 448f1ae32a1SGerd Hoffmann data[1] = 0x00; 449f1ae32a1SGerd Hoffmann ret = 2; 450f1ae32a1SGerd Hoffmann } 451f1ae32a1SGerd Hoffmann break; 452f1ae32a1SGerd Hoffmann } 453f1ae32a1SGerd Hoffmann 454f1ae32a1SGerd Hoffmann return ret; 455f1ae32a1SGerd Hoffmann } 456f1ae32a1SGerd Hoffmann static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, 457f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 458f1ae32a1SGerd Hoffmann int length, uint8_t *data) 459f1ae32a1SGerd Hoffmann { 460f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 461f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 462f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 463f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 464f1ae32a1SGerd Hoffmann bool set_vol = false; 465f1ae32a1SGerd Hoffmann 466f1ae32a1SGerd Hoffmann switch (aid) { 467f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): 468f1ae32a1SGerd Hoffmann s->out.mute = data[0] & 1; 469f1ae32a1SGerd Hoffmann set_vol = true; 470f1ae32a1SGerd Hoffmann ret = 0; 471f1ae32a1SGerd Hoffmann break; 472f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): 473f1ae32a1SGerd Hoffmann if (cn < 2) { 474f1ae32a1SGerd Hoffmann uint16_t vol = data[0] + (data[1] << 8); 475f1ae32a1SGerd Hoffmann 476f1ae32a1SGerd Hoffmann if (s->debug) { 477f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol); 478f1ae32a1SGerd Hoffmann } 479f1ae32a1SGerd Hoffmann 480f1ae32a1SGerd Hoffmann vol -= 0x8000; 481f1ae32a1SGerd Hoffmann vol = (vol * 255 + 0x4400) / 0x8800; 482f1ae32a1SGerd Hoffmann if (vol > 255) { 483f1ae32a1SGerd Hoffmann vol = 255; 484f1ae32a1SGerd Hoffmann } 485f1ae32a1SGerd Hoffmann 486f1ae32a1SGerd Hoffmann s->out.vol[cn] = vol; 487f1ae32a1SGerd Hoffmann set_vol = true; 488f1ae32a1SGerd Hoffmann ret = 0; 489f1ae32a1SGerd Hoffmann } 490f1ae32a1SGerd Hoffmann break; 491f1ae32a1SGerd Hoffmann } 492f1ae32a1SGerd Hoffmann 493f1ae32a1SGerd Hoffmann if (set_vol) { 494f1ae32a1SGerd Hoffmann if (s->debug) { 495f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n", 496f1ae32a1SGerd Hoffmann s->out.mute, s->out.vol[0], s->out.vol[1]); 497f1ae32a1SGerd Hoffmann } 498f1ae32a1SGerd Hoffmann AUD_set_volume_out(s->out.voice, s->out.mute, 499f1ae32a1SGerd Hoffmann s->out.vol[0], s->out.vol[1]); 500f1ae32a1SGerd Hoffmann } 501f1ae32a1SGerd Hoffmann 502f1ae32a1SGerd Hoffmann return ret; 503f1ae32a1SGerd Hoffmann } 504f1ae32a1SGerd Hoffmann 5059a77a0f5SHans de Goede static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, 506f1ae32a1SGerd Hoffmann int request, int value, int index, 507f1ae32a1SGerd Hoffmann int length, uint8_t *data) 508f1ae32a1SGerd Hoffmann { 509f1ae32a1SGerd Hoffmann USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); 510f1ae32a1SGerd Hoffmann int ret = 0; 511f1ae32a1SGerd Hoffmann 512f1ae32a1SGerd Hoffmann if (s->debug) { 513f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: control transaction: " 514f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 515f1ae32a1SGerd Hoffmann request, value, index, length); 516f1ae32a1SGerd Hoffmann } 517f1ae32a1SGerd Hoffmann 518f1ae32a1SGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 519f1ae32a1SGerd Hoffmann if (ret >= 0) { 5209a77a0f5SHans de Goede return; 521f1ae32a1SGerd Hoffmann } 522f1ae32a1SGerd Hoffmann 523f1ae32a1SGerd Hoffmann switch (request) { 524f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_CUR: 525f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MIN: 526f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MAX: 527f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_RES: 528f1ae32a1SGerd Hoffmann ret = usb_audio_get_control(s, request & 0xff, value, index, 529f1ae32a1SGerd Hoffmann length, data); 530f1ae32a1SGerd Hoffmann if (ret < 0) { 531f1ae32a1SGerd Hoffmann if (s->debug) { 532f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: get control\n"); 533f1ae32a1SGerd Hoffmann } 534f1ae32a1SGerd Hoffmann goto fail; 535f1ae32a1SGerd Hoffmann } 5369a77a0f5SHans de Goede p->actual_length = ret; 537f1ae32a1SGerd Hoffmann break; 538f1ae32a1SGerd Hoffmann 539f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_CUR: 540f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MIN: 541f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MAX: 542f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_RES: 543f1ae32a1SGerd Hoffmann ret = usb_audio_set_control(s, request & 0xff, value, index, 544f1ae32a1SGerd Hoffmann length, data); 545f1ae32a1SGerd Hoffmann if (ret < 0) { 546f1ae32a1SGerd Hoffmann if (s->debug) { 547f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: set control\n"); 548f1ae32a1SGerd Hoffmann } 549f1ae32a1SGerd Hoffmann goto fail; 550f1ae32a1SGerd Hoffmann } 551f1ae32a1SGerd Hoffmann break; 552f1ae32a1SGerd Hoffmann 553f1ae32a1SGerd Hoffmann default: 554f1ae32a1SGerd Hoffmann fail: 555f1ae32a1SGerd Hoffmann if (s->debug) { 556f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed control transaction: " 557f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 558f1ae32a1SGerd Hoffmann request, value, index, length); 559f1ae32a1SGerd Hoffmann } 5609a77a0f5SHans de Goede p->status = USB_RET_STALL; 561f1ae32a1SGerd Hoffmann break; 562f1ae32a1SGerd Hoffmann } 563f1ae32a1SGerd Hoffmann } 564f1ae32a1SGerd Hoffmann 565f1ae32a1SGerd Hoffmann static void usb_audio_set_interface(USBDevice *dev, int iface, 566f1ae32a1SGerd Hoffmann int old, int value) 567f1ae32a1SGerd Hoffmann { 568f1ae32a1SGerd Hoffmann USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); 569f1ae32a1SGerd Hoffmann 570f1ae32a1SGerd Hoffmann if (iface == 1) { 571f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, value); 572f1ae32a1SGerd Hoffmann } 573f1ae32a1SGerd Hoffmann } 574f1ae32a1SGerd Hoffmann 575f1ae32a1SGerd Hoffmann static void usb_audio_handle_reset(USBDevice *dev) 576f1ae32a1SGerd Hoffmann { 577f1ae32a1SGerd Hoffmann USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); 578f1ae32a1SGerd Hoffmann 579f1ae32a1SGerd Hoffmann if (s->debug) { 580f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: reset\n"); 581f1ae32a1SGerd Hoffmann } 582f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 583f1ae32a1SGerd Hoffmann } 584f1ae32a1SGerd Hoffmann 5859a77a0f5SHans de Goede static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) 586f1ae32a1SGerd Hoffmann { 587f1ae32a1SGerd Hoffmann if (s->out.altset == ALTSET_OFF) { 5889a77a0f5SHans de Goede p->status = USB_RET_STALL; 5899a77a0f5SHans de Goede return; 590f1ae32a1SGerd Hoffmann } 591f1ae32a1SGerd Hoffmann 5929a77a0f5SHans de Goede streambuf_put(&s->out.buf, p); 5939a77a0f5SHans de Goede if (p->actual_length < p->iov.size && s->debug > 1) { 594f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", 5959a77a0f5SHans de Goede p->iov.size - p->actual_length); 5969a77a0f5SHans de Goede } 597f1ae32a1SGerd Hoffmann } 598f1ae32a1SGerd Hoffmann 5999a77a0f5SHans de Goede static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) 600f1ae32a1SGerd Hoffmann { 601f1ae32a1SGerd Hoffmann USBAudioState *s = (USBAudioState *) dev; 602f1ae32a1SGerd Hoffmann 6039a77a0f5SHans de Goede if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { 6049a77a0f5SHans de Goede usb_audio_handle_dataout(s, p); 6059a77a0f5SHans de Goede return; 606f1ae32a1SGerd Hoffmann } 607f1ae32a1SGerd Hoffmann 6089a77a0f5SHans de Goede p->status = USB_RET_STALL; 6099a77a0f5SHans de Goede if (s->debug) { 610f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed data transaction: " 611f1ae32a1SGerd Hoffmann "pid 0x%x ep 0x%x len 0x%zx\n", 612f1ae32a1SGerd Hoffmann p->pid, p->ep->nr, p->iov.size); 613f1ae32a1SGerd Hoffmann } 614f1ae32a1SGerd Hoffmann } 615f1ae32a1SGerd Hoffmann 616f1ae32a1SGerd Hoffmann static void usb_audio_handle_destroy(USBDevice *dev) 617f1ae32a1SGerd Hoffmann { 618f1ae32a1SGerd Hoffmann USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); 619f1ae32a1SGerd Hoffmann 620f1ae32a1SGerd Hoffmann if (s->debug) { 621f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: destroy\n"); 622f1ae32a1SGerd Hoffmann } 623f1ae32a1SGerd Hoffmann 624f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 625f1ae32a1SGerd Hoffmann AUD_close_out(&s->card, s->out.voice); 626f1ae32a1SGerd Hoffmann AUD_remove_card(&s->card); 627f1ae32a1SGerd Hoffmann 628f1ae32a1SGerd Hoffmann streambuf_fini(&s->out.buf); 629f1ae32a1SGerd Hoffmann } 630f1ae32a1SGerd Hoffmann 631f1ae32a1SGerd Hoffmann static int usb_audio_initfn(USBDevice *dev) 632f1ae32a1SGerd Hoffmann { 633f1ae32a1SGerd Hoffmann USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev); 634f1ae32a1SGerd Hoffmann 6359d55d1adSGerd Hoffmann usb_desc_create_serial(dev); 636f1ae32a1SGerd Hoffmann usb_desc_init(dev); 637f1ae32a1SGerd Hoffmann s->dev.opaque = s; 638f1ae32a1SGerd Hoffmann AUD_register_card("usb-audio", &s->card); 639f1ae32a1SGerd Hoffmann 640f1ae32a1SGerd Hoffmann s->out.altset = ALTSET_OFF; 641f1ae32a1SGerd Hoffmann s->out.mute = false; 642f1ae32a1SGerd Hoffmann s->out.vol[0] = 240; /* 0 dB */ 643f1ae32a1SGerd Hoffmann s->out.vol[1] = 240; /* 0 dB */ 644f1ae32a1SGerd Hoffmann s->out.as.freq = USBAUDIO_SAMPLE_RATE; 645f1ae32a1SGerd Hoffmann s->out.as.nchannels = 2; 646f1ae32a1SGerd Hoffmann s->out.as.fmt = AUD_FMT_S16; 647f1ae32a1SGerd Hoffmann s->out.as.endianness = 0; 648f1ae32a1SGerd Hoffmann streambuf_init(&s->out.buf, s->buffer); 649f1ae32a1SGerd Hoffmann 650f1ae32a1SGerd Hoffmann s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio", 651f1ae32a1SGerd Hoffmann s, output_callback, &s->out.as); 652f1ae32a1SGerd Hoffmann AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]); 653f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, 0); 654f1ae32a1SGerd Hoffmann return 0; 655f1ae32a1SGerd Hoffmann } 656f1ae32a1SGerd Hoffmann 657f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_audio = { 658f1ae32a1SGerd Hoffmann .name = "usb-audio", 659f1ae32a1SGerd Hoffmann .unmigratable = 1, 660f1ae32a1SGerd Hoffmann }; 661f1ae32a1SGerd Hoffmann 662f1ae32a1SGerd Hoffmann static Property usb_audio_properties[] = { 663f1ae32a1SGerd Hoffmann DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), 664f1ae32a1SGerd Hoffmann DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, 665f1ae32a1SGerd Hoffmann 8 * USBAUDIO_PACKET_SIZE), 666f1ae32a1SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 667f1ae32a1SGerd Hoffmann }; 668f1ae32a1SGerd Hoffmann 669f1ae32a1SGerd Hoffmann static void usb_audio_class_init(ObjectClass *klass, void *data) 670f1ae32a1SGerd Hoffmann { 671f1ae32a1SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 672f1ae32a1SGerd Hoffmann USBDeviceClass *k = USB_DEVICE_CLASS(klass); 673f1ae32a1SGerd Hoffmann 674f1ae32a1SGerd Hoffmann dc->vmsd = &vmstate_usb_audio; 675f1ae32a1SGerd Hoffmann dc->props = usb_audio_properties; 676125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 677f1ae32a1SGerd Hoffmann k->product_desc = "QEMU USB Audio Interface"; 678f1ae32a1SGerd Hoffmann k->usb_desc = &desc_audio; 679f1ae32a1SGerd Hoffmann k->init = usb_audio_initfn; 680f1ae32a1SGerd Hoffmann k->handle_reset = usb_audio_handle_reset; 681f1ae32a1SGerd Hoffmann k->handle_control = usb_audio_handle_control; 682f1ae32a1SGerd Hoffmann k->handle_data = usb_audio_handle_data; 683f1ae32a1SGerd Hoffmann k->handle_destroy = usb_audio_handle_destroy; 684f1ae32a1SGerd Hoffmann k->set_interface = usb_audio_set_interface; 685f1ae32a1SGerd Hoffmann } 686f1ae32a1SGerd Hoffmann 6878c43a6f0SAndreas Färber static const TypeInfo usb_audio_info = { 688f1ae32a1SGerd Hoffmann .name = "usb-audio", 689f1ae32a1SGerd Hoffmann .parent = TYPE_USB_DEVICE, 690f1ae32a1SGerd Hoffmann .instance_size = sizeof(USBAudioState), 691f1ae32a1SGerd Hoffmann .class_init = usb_audio_class_init, 692f1ae32a1SGerd Hoffmann }; 693f1ae32a1SGerd Hoffmann 694f1ae32a1SGerd Hoffmann static void usb_audio_register_types(void) 695f1ae32a1SGerd Hoffmann { 696f1ae32a1SGerd Hoffmann type_register_static(&usb_audio_info); 697f1ae32a1SGerd Hoffmann usb_legacy_register("usb-audio", "audio", NULL); 698f1ae32a1SGerd Hoffmann } 699f1ae32a1SGerd Hoffmann 700f1ae32a1SGerd Hoffmann type_init(usb_audio_register_types) 701