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 32e532b2e0SPeter Maydell #include "qemu/osdep.h" 330b8fa32fSMarkus Armbruster #include "qemu/module.h" 34a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 35f1ae32a1SGerd Hoffmann #include "hw/usb.h" 36d6454270SMarkus Armbruster #include "migration/vmstate.h" 37463581a8SMichael S. Tsirkin #include "desc.h" 38f1ae32a1SGerd Hoffmann #include "audio/audio.h" 39db1015e9SEduardo Habkost #include "qom/object.h" 40f1ae32a1SGerd Hoffmann 413e44607eSKővágó, Zoltán static void usb_audio_reinit(USBDevice *dev, unsigned channels); 423e44607eSKővágó, Zoltán 43f1ae32a1SGerd Hoffmann #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ 44f1ae32a1SGerd Hoffmann #define USBAUDIO_PRODUCT_NUM 0x0002 45f1ae32a1SGerd Hoffmann 46f1ae32a1SGerd Hoffmann #define DEV_CONFIG_VALUE 1 /* The one and only */ 47f1ae32a1SGerd Hoffmann 483e44607eSKővágó, Zoltán #define USBAUDIO_MAX_CHANNELS(s) (s->multi ? 8 : 2) 493e44607eSKővágó, Zoltán 50f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AC interfaces */ 51f1ae32a1SGerd Hoffmann #define DST_AC_HEADER 1 52f1ae32a1SGerd Hoffmann #define DST_AC_INPUT_TERMINAL 2 53f1ae32a1SGerd Hoffmann #define DST_AC_OUTPUT_TERMINAL 3 54f1ae32a1SGerd Hoffmann #define DST_AC_FEATURE_UNIT 6 55f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AS interfaces */ 56f1ae32a1SGerd Hoffmann #define DST_AS_GENERAL 1 57f1ae32a1SGerd Hoffmann #define DST_AS_FORMAT_TYPE 2 58f1ae32a1SGerd Hoffmann /* Descriptor subtypes for endpoints */ 59f1ae32a1SGerd Hoffmann #define DST_EP_GENERAL 1 60f1ae32a1SGerd Hoffmann 61f1ae32a1SGerd Hoffmann enum usb_audio_strings { 62f1ae32a1SGerd Hoffmann STRING_NULL, 63f1ae32a1SGerd Hoffmann STRING_MANUFACTURER, 64f1ae32a1SGerd Hoffmann STRING_PRODUCT, 65f1ae32a1SGerd Hoffmann STRING_SERIALNUMBER, 66f1ae32a1SGerd Hoffmann STRING_CONFIG, 67f1ae32a1SGerd Hoffmann STRING_USBAUDIO_CONTROL, 68f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, 69f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, 70f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, 71f1ae32a1SGerd Hoffmann STRING_NULL_STREAM, 72f1ae32a1SGerd Hoffmann STRING_REAL_STREAM, 73f1ae32a1SGerd Hoffmann }; 74f1ae32a1SGerd Hoffmann 75f1ae32a1SGerd Hoffmann static const USBDescStrings usb_audio_stringtable = { 76f1ae32a1SGerd Hoffmann [STRING_MANUFACTURER] = "QEMU", 77f1ae32a1SGerd Hoffmann [STRING_PRODUCT] = "QEMU USB Audio", 78f1ae32a1SGerd Hoffmann [STRING_SERIALNUMBER] = "1", 79f1ae32a1SGerd Hoffmann [STRING_CONFIG] = "Audio Configuration", 80f1ae32a1SGerd Hoffmann [STRING_USBAUDIO_CONTROL] = "Audio Device", 81f1ae32a1SGerd Hoffmann [STRING_INPUT_TERMINAL] = "Audio Output Pipe", 82f1ae32a1SGerd Hoffmann [STRING_FEATURE_UNIT] = "Audio Output Volume Control", 83f1ae32a1SGerd Hoffmann [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", 84f1ae32a1SGerd Hoffmann [STRING_NULL_STREAM] = "Audio Output - Disabled", 85f1ae32a1SGerd Hoffmann [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", 86f1ae32a1SGerd Hoffmann }; 87f1ae32a1SGerd Hoffmann 883e44607eSKővágó, Zoltán /* 893e44607eSKővágó, Zoltán * A USB audio device supports an arbitrary number of alternate 903e44607eSKővágó, Zoltán * interface settings for each interface. Each corresponds to a block 913e44607eSKővágó, Zoltán * diagram of parameterized blocks. This can thus refer to things like 923e44607eSKővágó, Zoltán * number of channels, data rates, or in fact completely different 933e44607eSKővágó, Zoltán * block diagrams. Alternative setting 0 is always the null block diagram, 943e44607eSKővágó, Zoltán * which is used by a disabled device. 953e44607eSKővágó, Zoltán */ 963e44607eSKővágó, Zoltán enum usb_audio_altset { 973e44607eSKővágó, Zoltán ALTSET_OFF = 0x00, /* No endpoint */ 983e44607eSKővágó, Zoltán ALTSET_STEREO = 0x01, /* Single endpoint */ 993e44607eSKővágó, Zoltán ALTSET_51 = 0x02, 1003e44607eSKővágó, Zoltán ALTSET_71 = 0x03, 1013e44607eSKővágó, Zoltán }; 1023e44607eSKővágó, Zoltán 1033e44607eSKővágó, Zoltán static unsigned altset_channels[] = { 1043e44607eSKővágó, Zoltán [ALTSET_STEREO] = 2, 1053e44607eSKővágó, Zoltán [ALTSET_51] = 6, 1063e44607eSKővágó, Zoltán [ALTSET_71] = 8, 1073e44607eSKővágó, Zoltán }; 1083e44607eSKővágó, Zoltán 109f1ae32a1SGerd Hoffmann #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) 110f1ae32a1SGerd Hoffmann #define U24(x) U16(x), (((x) >> 16) & 0xff) 111f1ae32a1SGerd Hoffmann #define U32(x) U24(x), (((x) >> 24) & 0xff) 112f1ae32a1SGerd Hoffmann 113f1ae32a1SGerd Hoffmann /* 114f1ae32a1SGerd Hoffmann * A Basic Audio Device uses these specific values 115f1ae32a1SGerd Hoffmann */ 1163e44607eSKővágó, Zoltán #define USBAUDIO_PACKET_SIZE_BASE 96 1173e44607eSKővágó, Zoltán #define USBAUDIO_PACKET_SIZE(channels) (USBAUDIO_PACKET_SIZE_BASE * channels) 118f1ae32a1SGerd Hoffmann #define USBAUDIO_SAMPLE_RATE 48000 119f1ae32a1SGerd Hoffmann #define USBAUDIO_PACKET_INTERVAL 1 120f1ae32a1SGerd Hoffmann 121f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface[] = { 122f1ae32a1SGerd Hoffmann { 123f1ae32a1SGerd Hoffmann .bInterfaceNumber = 0, 124f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 125f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 126f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, 127f1ae32a1SGerd Hoffmann .bInterfaceProtocol = 0x04, 128f1ae32a1SGerd Hoffmann .iInterface = STRING_USBAUDIO_CONTROL, 129f1ae32a1SGerd Hoffmann .ndesc = 4, 130f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 131f1ae32a1SGerd Hoffmann { 132f1ae32a1SGerd Hoffmann /* Headphone Class-Specific AC Interface Header Descriptor */ 133f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 134f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 135f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 136f1ae32a1SGerd Hoffmann DST_AC_HEADER, /* u8 bDescriptorSubtype */ 137f1ae32a1SGerd Hoffmann U16(0x0100), /* u16 bcdADC */ 138f1ae32a1SGerd Hoffmann U16(0x2b), /* u16 wTotalLength */ 139f1ae32a1SGerd Hoffmann 0x01, /* u8 bInCollection */ 140f1ae32a1SGerd Hoffmann 0x01, /* u8 baInterfaceNr */ 141f1ae32a1SGerd Hoffmann } 142f1ae32a1SGerd Hoffmann },{ 143f1ae32a1SGerd Hoffmann /* Generic Stereo Input Terminal ID1 Descriptor */ 144f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 145f1ae32a1SGerd Hoffmann 0x0c, /* u8 bLength */ 146f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 147f1ae32a1SGerd Hoffmann DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ 148f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalID */ 149f1ae32a1SGerd Hoffmann U16(0x0101), /* u16 wTerminalType */ 150f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 1513e44607eSKővágó, Zoltán 0x02, /* u8 bNrChannels */ 152f1ae32a1SGerd Hoffmann U16(0x0003), /* u16 wChannelConfig */ 153f1ae32a1SGerd Hoffmann 0x00, /* u8 iChannelNames */ 154f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, /* u8 iTerminal */ 155f1ae32a1SGerd Hoffmann } 156f1ae32a1SGerd Hoffmann },{ 157f1ae32a1SGerd Hoffmann /* Generic Stereo Feature Unit ID2 Descriptor */ 158f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 159f1ae32a1SGerd Hoffmann 0x0d, /* u8 bLength */ 160f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 161f1ae32a1SGerd Hoffmann DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ 162f1ae32a1SGerd Hoffmann 0x02, /* u8 bUnitID */ 163f1ae32a1SGerd Hoffmann 0x01, /* u8 bSourceID */ 164f1ae32a1SGerd Hoffmann 0x02, /* u8 bControlSize */ 165f1ae32a1SGerd Hoffmann U16(0x0001), /* u16 bmaControls(0) */ 166f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(1) */ 167f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(2) */ 168f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, /* u8 iFeature */ 169f1ae32a1SGerd Hoffmann } 170f1ae32a1SGerd Hoffmann },{ 171ae420c95SCai Huoqing /* Headphone Output Terminal ID3 Descriptor */ 172f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 173f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 174f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 175f1ae32a1SGerd Hoffmann DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ 176f1ae32a1SGerd Hoffmann 0x03, /* u8 bUnitID */ 177f1ae32a1SGerd Hoffmann U16(0x0301), /* u16 wTerminalType (SPK) */ 178f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 179f1ae32a1SGerd Hoffmann 0x02, /* u8 bSourceID */ 180f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ 181f1ae32a1SGerd Hoffmann } 182f1ae32a1SGerd Hoffmann } 183f1ae32a1SGerd Hoffmann }, 184f1ae32a1SGerd Hoffmann },{ 185f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 1863e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_OFF, 187f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 188f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 189f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 190f1ae32a1SGerd Hoffmann .iInterface = STRING_NULL_STREAM, 191f1ae32a1SGerd Hoffmann },{ 192f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 1933e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_STEREO, 194f1ae32a1SGerd Hoffmann .bNumEndpoints = 1, 195f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 196f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 197f1ae32a1SGerd Hoffmann .iInterface = STRING_REAL_STREAM, 198f1ae32a1SGerd Hoffmann .ndesc = 2, 199f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 200f1ae32a1SGerd Hoffmann { 201f1ae32a1SGerd Hoffmann /* Headphone Class-specific AS General Interface Descriptor */ 202f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 203f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 204f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 205f1ae32a1SGerd Hoffmann DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 206f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalLink */ 207f1ae32a1SGerd Hoffmann 0x00, /* u8 bDelay */ 208f1ae32a1SGerd Hoffmann 0x01, 0x00, /* u16 wFormatTag */ 209f1ae32a1SGerd Hoffmann } 210f1ae32a1SGerd Hoffmann },{ 211f1ae32a1SGerd Hoffmann /* Headphone Type I Format Type Descriptor */ 212f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 213f1ae32a1SGerd Hoffmann 0x0b, /* u8 bLength */ 214f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 215f1ae32a1SGerd Hoffmann DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 216f1ae32a1SGerd Hoffmann 0x01, /* u8 bFormatType */ 217f1ae32a1SGerd Hoffmann 0x02, /* u8 bNrChannels */ 218f1ae32a1SGerd Hoffmann 0x02, /* u8 bSubFrameSize */ 219f1ae32a1SGerd Hoffmann 0x10, /* u8 bBitResolution */ 220f1ae32a1SGerd Hoffmann 0x01, /* u8 bSamFreqType */ 221f1ae32a1SGerd Hoffmann U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 222f1ae32a1SGerd Hoffmann } 223f1ae32a1SGerd Hoffmann } 224f1ae32a1SGerd Hoffmann }, 225f1ae32a1SGerd Hoffmann .eps = (USBDescEndpoint[]) { 226f1ae32a1SGerd Hoffmann { 227f1ae32a1SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | 0x01, 228f1ae32a1SGerd Hoffmann .bmAttributes = 0x0d, 2293e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), 230f1ae32a1SGerd Hoffmann .bInterval = 1, 231f1ae32a1SGerd Hoffmann .is_audio = 1, 232f1ae32a1SGerd Hoffmann /* Stereo Headphone Class-specific 233f1ae32a1SGerd Hoffmann AS Audio Data Endpoint Descriptor */ 234f1ae32a1SGerd Hoffmann .extra = (uint8_t[]) { 235f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 236f1ae32a1SGerd Hoffmann USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 237f1ae32a1SGerd Hoffmann DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 238f1ae32a1SGerd Hoffmann 0x00, /* u8 bmAttributes */ 239f1ae32a1SGerd Hoffmann 0x00, /* u8 bLockDelayUnits */ 240f1ae32a1SGerd Hoffmann U16(0x0000), /* u16 wLockDelay */ 241f1ae32a1SGerd Hoffmann }, 242f1ae32a1SGerd Hoffmann }, 243f1ae32a1SGerd Hoffmann } 244f1ae32a1SGerd Hoffmann } 245f1ae32a1SGerd Hoffmann }; 246f1ae32a1SGerd Hoffmann 247f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device = { 2482bbd086cSGerd Hoffmann .bcdUSB = 0x0100, 249f1ae32a1SGerd Hoffmann .bMaxPacketSize0 = 64, 250f1ae32a1SGerd Hoffmann .bNumConfigurations = 1, 251f1ae32a1SGerd Hoffmann .confs = (USBDescConfig[]) { 252f1ae32a1SGerd Hoffmann { 253f1ae32a1SGerd Hoffmann .bNumInterfaces = 2, 254f1ae32a1SGerd Hoffmann .bConfigurationValue = DEV_CONFIG_VALUE, 255f1ae32a1SGerd Hoffmann .iConfiguration = STRING_CONFIG, 256bd93976aSPantelis Koukousoulas .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, 257f1ae32a1SGerd Hoffmann .bMaxPower = 0x32, 258f1ae32a1SGerd Hoffmann .nif = ARRAY_SIZE(desc_iface), 259f1ae32a1SGerd Hoffmann .ifs = desc_iface, 260f1ae32a1SGerd Hoffmann }, 261f1ae32a1SGerd Hoffmann }, 262f1ae32a1SGerd Hoffmann }; 263f1ae32a1SGerd Hoffmann 264f1ae32a1SGerd Hoffmann static const USBDesc desc_audio = { 265f1ae32a1SGerd Hoffmann .id = { 266f1ae32a1SGerd Hoffmann .idVendor = USBAUDIO_VENDOR_NUM, 267f1ae32a1SGerd Hoffmann .idProduct = USBAUDIO_PRODUCT_NUM, 268f1ae32a1SGerd Hoffmann .bcdDevice = 0, 269f1ae32a1SGerd Hoffmann .iManufacturer = STRING_MANUFACTURER, 270f1ae32a1SGerd Hoffmann .iProduct = STRING_PRODUCT, 271f1ae32a1SGerd Hoffmann .iSerialNumber = STRING_SERIALNUMBER, 272f1ae32a1SGerd Hoffmann }, 273f1ae32a1SGerd Hoffmann .full = &desc_device, 274f1ae32a1SGerd Hoffmann .str = usb_audio_stringtable, 275f1ae32a1SGerd Hoffmann }; 276f1ae32a1SGerd Hoffmann 2773e44607eSKővágó, Zoltán /* multi channel compatible desc */ 2783e44607eSKővágó, Zoltán 2793e44607eSKővágó, Zoltán static const USBDescIface desc_iface_multi[] = { 2803e44607eSKővágó, Zoltán { 2813e44607eSKővágó, Zoltán .bInterfaceNumber = 0, 2823e44607eSKővágó, Zoltán .bNumEndpoints = 0, 2833e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 2843e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, 2853e44607eSKővágó, Zoltán .bInterfaceProtocol = 0x04, 2863e44607eSKővágó, Zoltán .iInterface = STRING_USBAUDIO_CONTROL, 2873e44607eSKővágó, Zoltán .ndesc = 4, 2883e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 2893e44607eSKővágó, Zoltán { 2903e44607eSKővágó, Zoltán /* Headphone Class-Specific AC Interface Header Descriptor */ 2913e44607eSKővágó, Zoltán .data = (uint8_t[]) { 2923e44607eSKővágó, Zoltán 0x09, /* u8 bLength */ 2933e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 2943e44607eSKővágó, Zoltán DST_AC_HEADER, /* u8 bDescriptorSubtype */ 2953e44607eSKővágó, Zoltán U16(0x0100), /* u16 bcdADC */ 2963e44607eSKővágó, Zoltán U16(0x38), /* u16 wTotalLength */ 2973e44607eSKővágó, Zoltán 0x01, /* u8 bInCollection */ 2983e44607eSKővágó, Zoltán 0x01, /* u8 baInterfaceNr */ 2993e44607eSKővágó, Zoltán } 3003e44607eSKővágó, Zoltán },{ 3013e44607eSKővágó, Zoltán /* Generic Stereo Input Terminal ID1 Descriptor */ 3023e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3033e44607eSKővágó, Zoltán 0x0c, /* u8 bLength */ 3043e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3053e44607eSKővágó, Zoltán DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ 3063e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalID */ 3073e44607eSKővágó, Zoltán U16(0x0101), /* u16 wTerminalType */ 3083e44607eSKővágó, Zoltán 0x00, /* u8 bAssocTerminal */ 3093e44607eSKővágó, Zoltán 0x08, /* u8 bNrChannels */ 3103e44607eSKővágó, Zoltán U16(0x063f), /* u16 wChannelConfig */ 3113e44607eSKővágó, Zoltán 0x00, /* u8 iChannelNames */ 3123e44607eSKővágó, Zoltán STRING_INPUT_TERMINAL, /* u8 iTerminal */ 3133e44607eSKővágó, Zoltán } 3143e44607eSKővágó, Zoltán },{ 3153e44607eSKővágó, Zoltán /* Generic Stereo Feature Unit ID2 Descriptor */ 3163e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3173e44607eSKővágó, Zoltán 0x19, /* u8 bLength */ 3183e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3193e44607eSKővágó, Zoltán DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ 3203e44607eSKővágó, Zoltán 0x02, /* u8 bUnitID */ 3213e44607eSKővágó, Zoltán 0x01, /* u8 bSourceID */ 3223e44607eSKővágó, Zoltán 0x02, /* u8 bControlSize */ 3233e44607eSKővágó, Zoltán U16(0x0001), /* u16 bmaControls(0) */ 3243e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(1) */ 3253e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(2) */ 3263e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(3) */ 3273e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(4) */ 3283e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(5) */ 3293e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(6) */ 3303e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(7) */ 3313e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(8) */ 3323e44607eSKővágó, Zoltán STRING_FEATURE_UNIT, /* u8 iFeature */ 3333e44607eSKővágó, Zoltán } 3343e44607eSKővágó, Zoltán },{ 335ae420c95SCai Huoqing /* Headphone Output Terminal ID3 Descriptor */ 3363e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3373e44607eSKővágó, Zoltán 0x09, /* u8 bLength */ 3383e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3393e44607eSKővágó, Zoltán DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ 3403e44607eSKővágó, Zoltán 0x03, /* u8 bUnitID */ 3413e44607eSKővágó, Zoltán U16(0x0301), /* u16 wTerminalType (SPK) */ 3423e44607eSKővágó, Zoltán 0x00, /* u8 bAssocTerminal */ 3433e44607eSKővágó, Zoltán 0x02, /* u8 bSourceID */ 3443e44607eSKővágó, Zoltán STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ 3453e44607eSKővágó, Zoltán } 3463e44607eSKővágó, Zoltán } 3473e44607eSKővágó, Zoltán }, 3483e44607eSKővágó, Zoltán },{ 3493e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 3503e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_OFF, 3513e44607eSKővágó, Zoltán .bNumEndpoints = 0, 3523e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 3533e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 3543e44607eSKővágó, Zoltán .iInterface = STRING_NULL_STREAM, 3553e44607eSKővágó, Zoltán },{ 3563e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 3573e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_STEREO, 3583e44607eSKővágó, Zoltán .bNumEndpoints = 1, 3593e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 3603e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 3613e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 3623e44607eSKővágó, Zoltán .ndesc = 2, 3633e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 3643e44607eSKővágó, Zoltán { 3653e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 3663e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3673e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 3683e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3693e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 3703e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 3713e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 3723e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 3733e44607eSKővágó, Zoltán } 3743e44607eSKővágó, Zoltán },{ 3753e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 3763e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3773e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 3783e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3793e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 3803e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 3813e44607eSKővágó, Zoltán 0x02, /* u8 bNrChannels */ 3823e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 3833e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 3843e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 3853e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 3863e44607eSKővágó, Zoltán } 3873e44607eSKővágó, Zoltán } 3883e44607eSKővágó, Zoltán }, 3893e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 3903e44607eSKővágó, Zoltán { 3913e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 3923e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 3933e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), 3943e44607eSKővágó, Zoltán .bInterval = 1, 3953e44607eSKővágó, Zoltán .is_audio = 1, 3963e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 3973e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 3983e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 3993e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4003e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 4013e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 4023e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 4033e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 4043e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 4053e44607eSKővágó, Zoltán }, 4063e44607eSKővágó, Zoltán }, 4073e44607eSKővágó, Zoltán } 4083e44607eSKővágó, Zoltán },{ 4093e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 4103e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_51, 4113e44607eSKővágó, Zoltán .bNumEndpoints = 1, 4123e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 4133e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 4143e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 4153e44607eSKővágó, Zoltán .ndesc = 2, 4163e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 4173e44607eSKővágó, Zoltán { 4183e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 4193e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4203e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4213e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4223e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 4233e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 4243e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 4253e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 4263e44607eSKővágó, Zoltán } 4273e44607eSKővágó, Zoltán },{ 4283e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 4293e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4303e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 4313e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4323e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 4333e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 4343e44607eSKővágó, Zoltán 0x06, /* u8 bNrChannels */ 4353e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 4363e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 4373e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 4383e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 4393e44607eSKővágó, Zoltán } 4403e44607eSKővágó, Zoltán } 4413e44607eSKővágó, Zoltán }, 4423e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 4433e44607eSKővágó, Zoltán { 4443e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 4453e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 4463e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(6), 4473e44607eSKővágó, Zoltán .bInterval = 1, 4483e44607eSKővágó, Zoltán .is_audio = 1, 4493e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 4503e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 4513e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 4523e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4533e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 4543e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 4553e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 4563e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 4573e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 4583e44607eSKővágó, Zoltán }, 4593e44607eSKővágó, Zoltán }, 4603e44607eSKővágó, Zoltán } 4613e44607eSKővágó, Zoltán },{ 4623e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 4633e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_71, 4643e44607eSKővágó, Zoltán .bNumEndpoints = 1, 4653e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 4663e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 4673e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 4683e44607eSKővágó, Zoltán .ndesc = 2, 4693e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 4703e44607eSKővágó, Zoltán { 4713e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 4723e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4733e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4743e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4753e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 4763e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 4773e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 4783e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 4793e44607eSKővágó, Zoltán } 4803e44607eSKővágó, Zoltán },{ 4813e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 4823e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4833e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 4843e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4853e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 4863e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 4873e44607eSKővágó, Zoltán 0x08, /* u8 bNrChannels */ 4883e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 4893e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 4903e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 4913e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 4923e44607eSKővágó, Zoltán } 4933e44607eSKővágó, Zoltán } 4943e44607eSKővágó, Zoltán }, 4953e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 4963e44607eSKővágó, Zoltán { 4973e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 4983e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 4993e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(8), 5003e44607eSKővágó, Zoltán .bInterval = 1, 5013e44607eSKővágó, Zoltán .is_audio = 1, 5023e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 5033e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 5043e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 5053e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 5063e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 5073e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 5083e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 5093e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 5103e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 5113e44607eSKővágó, Zoltán }, 5123e44607eSKővágó, Zoltán }, 5133e44607eSKővágó, Zoltán } 5143e44607eSKővágó, Zoltán } 5153e44607eSKővágó, Zoltán }; 5163e44607eSKővágó, Zoltán 5173e44607eSKővágó, Zoltán static const USBDescDevice desc_device_multi = { 5183e44607eSKővágó, Zoltán .bcdUSB = 0x0100, 5193e44607eSKővágó, Zoltán .bMaxPacketSize0 = 64, 5203e44607eSKővágó, Zoltán .bNumConfigurations = 1, 5213e44607eSKővágó, Zoltán .confs = (USBDescConfig[]) { 5223e44607eSKővágó, Zoltán { 5233e44607eSKővágó, Zoltán .bNumInterfaces = 2, 5243e44607eSKővágó, Zoltán .bConfigurationValue = DEV_CONFIG_VALUE, 5253e44607eSKővágó, Zoltán .iConfiguration = STRING_CONFIG, 5263e44607eSKővágó, Zoltán .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, 5273e44607eSKővágó, Zoltán .bMaxPower = 0x32, 5283e44607eSKővágó, Zoltán .nif = ARRAY_SIZE(desc_iface_multi), 5293e44607eSKővágó, Zoltán .ifs = desc_iface_multi, 5303e44607eSKővágó, Zoltán } 5313e44607eSKővágó, Zoltán }, 5323e44607eSKővágó, Zoltán }; 5333e44607eSKővágó, Zoltán 5343e44607eSKővágó, Zoltán static const USBDesc desc_audio_multi = { 5353e44607eSKővágó, Zoltán .id = { 5363e44607eSKővágó, Zoltán .idVendor = USBAUDIO_VENDOR_NUM, 5373e44607eSKővágó, Zoltán .idProduct = USBAUDIO_PRODUCT_NUM, 5383e44607eSKővágó, Zoltán .bcdDevice = 0, 5393e44607eSKővágó, Zoltán .iManufacturer = STRING_MANUFACTURER, 5403e44607eSKővágó, Zoltán .iProduct = STRING_PRODUCT, 5413e44607eSKővágó, Zoltán .iSerialNumber = STRING_SERIALNUMBER, 5423e44607eSKővágó, Zoltán }, 5433e44607eSKővágó, Zoltán .full = &desc_device_multi, 5443e44607eSKővágó, Zoltán .str = usb_audio_stringtable, 545f1ae32a1SGerd Hoffmann }; 546f1ae32a1SGerd Hoffmann 547f1ae32a1SGerd Hoffmann /* 548f1ae32a1SGerd Hoffmann * Class-specific control requests 549f1ae32a1SGerd Hoffmann */ 550f1ae32a1SGerd Hoffmann #define CR_SET_CUR 0x01 551f1ae32a1SGerd Hoffmann #define CR_GET_CUR 0x81 552f1ae32a1SGerd Hoffmann #define CR_SET_MIN 0x02 553f1ae32a1SGerd Hoffmann #define CR_GET_MIN 0x82 554f1ae32a1SGerd Hoffmann #define CR_SET_MAX 0x03 555f1ae32a1SGerd Hoffmann #define CR_GET_MAX 0x83 556f1ae32a1SGerd Hoffmann #define CR_SET_RES 0x04 557f1ae32a1SGerd Hoffmann #define CR_GET_RES 0x84 558f1ae32a1SGerd Hoffmann #define CR_SET_MEM 0x05 559f1ae32a1SGerd Hoffmann #define CR_GET_MEM 0x85 560f1ae32a1SGerd Hoffmann #define CR_GET_STAT 0xff 561f1ae32a1SGerd Hoffmann 562f1ae32a1SGerd Hoffmann /* 563f1ae32a1SGerd Hoffmann * Feature Unit Control Selectors 564f1ae32a1SGerd Hoffmann */ 565f1ae32a1SGerd Hoffmann #define MUTE_CONTROL 0x01 566f1ae32a1SGerd Hoffmann #define VOLUME_CONTROL 0x02 567f1ae32a1SGerd Hoffmann #define BASS_CONTROL 0x03 568f1ae32a1SGerd Hoffmann #define MID_CONTROL 0x04 569f1ae32a1SGerd Hoffmann #define TREBLE_CONTROL 0x05 570f1ae32a1SGerd Hoffmann #define GRAPHIC_EQUALIZER_CONTROL 0x06 571f1ae32a1SGerd Hoffmann #define AUTOMATIC_GAIN_CONTROL 0x07 572f1ae32a1SGerd Hoffmann #define DELAY_CONTROL 0x08 573f1ae32a1SGerd Hoffmann #define BASS_BOOST_CONTROL 0x09 574f1ae32a1SGerd Hoffmann #define LOUDNESS_CONTROL 0x0a 575f1ae32a1SGerd Hoffmann 576f1ae32a1SGerd Hoffmann /* 577f1ae32a1SGerd Hoffmann * buffering 578f1ae32a1SGerd Hoffmann */ 579f1ae32a1SGerd Hoffmann 580f1ae32a1SGerd Hoffmann struct streambuf { 581f1ae32a1SGerd Hoffmann uint8_t *data; 582670777a9SKővágó, Zoltán size_t size; 583670777a9SKővágó, Zoltán uint64_t prod; 584670777a9SKővágó, Zoltán uint64_t cons; 585f1ae32a1SGerd Hoffmann }; 586f1ae32a1SGerd Hoffmann 5873e44607eSKővágó, Zoltán static void streambuf_init(struct streambuf *buf, uint32_t size, 5883e44607eSKővágó, Zoltán uint32_t channels) 589f1ae32a1SGerd Hoffmann { 590f1ae32a1SGerd Hoffmann g_free(buf->data); 5913e44607eSKővágó, Zoltán buf->size = size - (size % USBAUDIO_PACKET_SIZE(channels)); 592f1ae32a1SGerd Hoffmann buf->data = g_malloc(buf->size); 593f1ae32a1SGerd Hoffmann buf->prod = 0; 594f1ae32a1SGerd Hoffmann buf->cons = 0; 595f1ae32a1SGerd Hoffmann } 596f1ae32a1SGerd Hoffmann 597f1ae32a1SGerd Hoffmann static void streambuf_fini(struct streambuf *buf) 598f1ae32a1SGerd Hoffmann { 599f1ae32a1SGerd Hoffmann g_free(buf->data); 600f1ae32a1SGerd Hoffmann buf->data = NULL; 601f1ae32a1SGerd Hoffmann } 602f1ae32a1SGerd Hoffmann 6033e44607eSKővágó, Zoltán static int streambuf_put(struct streambuf *buf, USBPacket *p, uint32_t channels) 604f1ae32a1SGerd Hoffmann { 605670777a9SKővágó, Zoltán int64_t free = buf->size - (buf->prod - buf->cons); 606f1ae32a1SGerd Hoffmann 6073e44607eSKővágó, Zoltán if (free < USBAUDIO_PACKET_SIZE(channels)) { 608f1ae32a1SGerd Hoffmann return 0; 609f1ae32a1SGerd Hoffmann } 6103e44607eSKővágó, Zoltán if (p->iov.size != USBAUDIO_PACKET_SIZE(channels)) { 611a7fde1c1SGerd Hoffmann return 0; 612a7fde1c1SGerd Hoffmann } 6132c6a740fSKővágó, Zoltán 614670777a9SKővágó, Zoltán /* can happen if prod overflows */ 615670777a9SKővágó, Zoltán assert(buf->prod % USBAUDIO_PACKET_SIZE(channels) == 0); 616f1ae32a1SGerd Hoffmann usb_packet_copy(p, buf->data + (buf->prod % buf->size), 6173e44607eSKővágó, Zoltán USBAUDIO_PACKET_SIZE(channels)); 6183e44607eSKővágó, Zoltán buf->prod += USBAUDIO_PACKET_SIZE(channels); 6193e44607eSKővágó, Zoltán return USBAUDIO_PACKET_SIZE(channels); 620f1ae32a1SGerd Hoffmann } 621f1ae32a1SGerd Hoffmann 6222c6a740fSKővágó, Zoltán static uint8_t *streambuf_get(struct streambuf *buf, size_t *len) 623f1ae32a1SGerd Hoffmann { 624670777a9SKővágó, Zoltán int64_t used = buf->prod - buf->cons; 625f1ae32a1SGerd Hoffmann uint8_t *data; 626f1ae32a1SGerd Hoffmann 627670777a9SKővágó, Zoltán if (used <= 0) { 6282c6a740fSKővágó, Zoltán *len = 0; 629f1ae32a1SGerd Hoffmann return NULL; 630f1ae32a1SGerd Hoffmann } 631f1ae32a1SGerd Hoffmann data = buf->data + (buf->cons % buf->size); 6322c6a740fSKővágó, Zoltán *len = MIN(buf->prod - buf->cons, 6332c6a740fSKővágó, Zoltán buf->size - (buf->cons % buf->size)); 634f1ae32a1SGerd Hoffmann return data; 635f1ae32a1SGerd Hoffmann } 636f1ae32a1SGerd Hoffmann 637db1015e9SEduardo Habkost struct USBAudioState { 638f1ae32a1SGerd Hoffmann /* qemu interfaces */ 639f1ae32a1SGerd Hoffmann USBDevice dev; 640f1ae32a1SGerd Hoffmann QEMUSoundCard card; 641f1ae32a1SGerd Hoffmann 642f1ae32a1SGerd Hoffmann /* state */ 643f1ae32a1SGerd Hoffmann struct { 644f1ae32a1SGerd Hoffmann enum usb_audio_altset altset; 645f1ae32a1SGerd Hoffmann struct audsettings as; 646f1ae32a1SGerd Hoffmann SWVoiceOut *voice; 6473e44607eSKővágó, Zoltán Volume vol; 648f1ae32a1SGerd Hoffmann struct streambuf buf; 6493e44607eSKővágó, Zoltán uint32_t channels; 650f1ae32a1SGerd Hoffmann } out; 651f1ae32a1SGerd Hoffmann 652f1ae32a1SGerd Hoffmann /* properties */ 653f1ae32a1SGerd Hoffmann uint32_t debug; 6543e44607eSKővágó, Zoltán uint32_t buffer_user, buffer; 6553e44607eSKővágó, Zoltán bool multi; 656db1015e9SEduardo Habkost }; 657f1ae32a1SGerd Hoffmann 6580389a0b1SGonglei #define TYPE_USB_AUDIO "usb-audio" 6598063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(USBAudioState, USB_AUDIO) 6600389a0b1SGonglei 661f1ae32a1SGerd Hoffmann static void output_callback(void *opaque, int avail) 662f1ae32a1SGerd Hoffmann { 663f1ae32a1SGerd Hoffmann USBAudioState *s = opaque; 664f1ae32a1SGerd Hoffmann uint8_t *data; 665f1ae32a1SGerd Hoffmann 6662c6a740fSKővágó, Zoltán while (avail) { 6672c6a740fSKővágó, Zoltán size_t written, len; 6682c6a740fSKővágó, Zoltán 6692c6a740fSKővágó, Zoltán data = streambuf_get(&s->out.buf, &len); 670d0657b2aSGonglei if (!data) { 671f1ae32a1SGerd Hoffmann return; 672f1ae32a1SGerd Hoffmann } 6732c6a740fSKővágó, Zoltán 6742c6a740fSKővágó, Zoltán written = AUD_write(s->out.voice, data, len); 6752c6a740fSKővágó, Zoltán avail -= written; 6762c6a740fSKővágó, Zoltán s->out.buf.cons += written; 6772c6a740fSKővágó, Zoltán 6782c6a740fSKővágó, Zoltán if (written < len) { 6792c6a740fSKővágó, Zoltán return; 6802c6a740fSKővágó, Zoltán } 681f1ae32a1SGerd Hoffmann } 682f1ae32a1SGerd Hoffmann } 683f1ae32a1SGerd Hoffmann 684f1ae32a1SGerd Hoffmann static int usb_audio_set_output_altset(USBAudioState *s, int altset) 685f1ae32a1SGerd Hoffmann { 686f1ae32a1SGerd Hoffmann switch (altset) { 687f1ae32a1SGerd Hoffmann case ALTSET_OFF: 688f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, false); 689f1ae32a1SGerd Hoffmann break; 6903e44607eSKővágó, Zoltán case ALTSET_STEREO: 6913e44607eSKővágó, Zoltán case ALTSET_51: 6923e44607eSKővágó, Zoltán case ALTSET_71: 6933e44607eSKővágó, Zoltán if (s->out.channels != altset_channels[altset]) { 6943e44607eSKővágó, Zoltán usb_audio_reinit(USB_DEVICE(s), altset_channels[altset]); 6953e44607eSKővágó, Zoltán } 6963e44607eSKővágó, Zoltán streambuf_init(&s->out.buf, s->buffer, s->out.channels); 697f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, true); 698f1ae32a1SGerd Hoffmann break; 699f1ae32a1SGerd Hoffmann default: 700f1ae32a1SGerd Hoffmann return -1; 701f1ae32a1SGerd Hoffmann } 702f1ae32a1SGerd Hoffmann 703f1ae32a1SGerd Hoffmann if (s->debug) { 704f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: set interface %d\n", altset); 705f1ae32a1SGerd Hoffmann } 706f1ae32a1SGerd Hoffmann s->out.altset = altset; 707f1ae32a1SGerd Hoffmann return 0; 708f1ae32a1SGerd Hoffmann } 709f1ae32a1SGerd Hoffmann 710f1ae32a1SGerd Hoffmann /* 711f1ae32a1SGerd Hoffmann * Note: we arbitrarily map the volume control range onto -inf..+8 dB 712f1ae32a1SGerd Hoffmann */ 713f1ae32a1SGerd Hoffmann #define ATTRIB_ID(cs, attrib, idif) \ 714f1ae32a1SGerd Hoffmann (((cs) << 24) | ((attrib) << 16) | (idif)) 715f1ae32a1SGerd Hoffmann 716f1ae32a1SGerd Hoffmann static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, 717f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 718f1ae32a1SGerd Hoffmann int length, uint8_t *data) 719f1ae32a1SGerd Hoffmann { 720f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 721f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 722f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 723f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 724f1ae32a1SGerd Hoffmann 725f1ae32a1SGerd Hoffmann switch (aid) { 726f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): 7273e44607eSKővágó, Zoltán data[0] = s->out.vol.mute; 728f1ae32a1SGerd Hoffmann ret = 1; 729f1ae32a1SGerd Hoffmann break; 730f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): 7313e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 7323e44607eSKővágó, Zoltán uint16_t vol = (s->out.vol.vol[cn] * 0x8800 + 127) / 255 + 0x8000; 733f1ae32a1SGerd Hoffmann data[0] = vol; 734f1ae32a1SGerd Hoffmann data[1] = vol >> 8; 735f1ae32a1SGerd Hoffmann ret = 2; 736f1ae32a1SGerd Hoffmann } 737f1ae32a1SGerd Hoffmann break; 738f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): 7393e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 740f1ae32a1SGerd Hoffmann data[0] = 0x01; 741f1ae32a1SGerd Hoffmann data[1] = 0x80; 742f1ae32a1SGerd Hoffmann ret = 2; 743f1ae32a1SGerd Hoffmann } 744f1ae32a1SGerd Hoffmann break; 745f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): 7463e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 747f1ae32a1SGerd Hoffmann data[0] = 0x00; 748f1ae32a1SGerd Hoffmann data[1] = 0x08; 749f1ae32a1SGerd Hoffmann ret = 2; 750f1ae32a1SGerd Hoffmann } 751f1ae32a1SGerd Hoffmann break; 752f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): 7533e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 754f1ae32a1SGerd Hoffmann data[0] = 0x88; 755f1ae32a1SGerd Hoffmann data[1] = 0x00; 756f1ae32a1SGerd Hoffmann ret = 2; 757f1ae32a1SGerd Hoffmann } 758f1ae32a1SGerd Hoffmann break; 759f1ae32a1SGerd Hoffmann } 760f1ae32a1SGerd Hoffmann 761f1ae32a1SGerd Hoffmann return ret; 762f1ae32a1SGerd Hoffmann } 763f1ae32a1SGerd Hoffmann static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, 764f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 765f1ae32a1SGerd Hoffmann int length, uint8_t *data) 766f1ae32a1SGerd Hoffmann { 767f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 768f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 769f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 770f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 771f1ae32a1SGerd Hoffmann bool set_vol = false; 772f1ae32a1SGerd Hoffmann 773f1ae32a1SGerd Hoffmann switch (aid) { 774f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): 7753e44607eSKővágó, Zoltán s->out.vol.mute = data[0] & 1; 776f1ae32a1SGerd Hoffmann set_vol = true; 777f1ae32a1SGerd Hoffmann ret = 0; 778f1ae32a1SGerd Hoffmann break; 779f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): 7803e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 781f1ae32a1SGerd Hoffmann uint16_t vol = data[0] + (data[1] << 8); 782f1ae32a1SGerd Hoffmann 783f1ae32a1SGerd Hoffmann if (s->debug) { 7843e44607eSKővágó, Zoltán fprintf(stderr, "usb-audio: cn %d vol %04x\n", cn, 7853e44607eSKővágó, Zoltán (uint16_t)vol); 786f1ae32a1SGerd Hoffmann } 787f1ae32a1SGerd Hoffmann 788f1ae32a1SGerd Hoffmann vol -= 0x8000; 789f1ae32a1SGerd Hoffmann vol = (vol * 255 + 0x4400) / 0x8800; 790f1ae32a1SGerd Hoffmann if (vol > 255) { 791f1ae32a1SGerd Hoffmann vol = 255; 792f1ae32a1SGerd Hoffmann } 793f1ae32a1SGerd Hoffmann 7943e44607eSKővágó, Zoltán s->out.vol.vol[cn] = vol; 795f1ae32a1SGerd Hoffmann set_vol = true; 796f1ae32a1SGerd Hoffmann ret = 0; 797f1ae32a1SGerd Hoffmann } 798f1ae32a1SGerd Hoffmann break; 799f1ae32a1SGerd Hoffmann } 800f1ae32a1SGerd Hoffmann 801f1ae32a1SGerd Hoffmann if (set_vol) { 802f1ae32a1SGerd Hoffmann if (s->debug) { 8033e44607eSKővágó, Zoltán int i; 8043e44607eSKővágó, Zoltán fprintf(stderr, "usb-audio: mute %d", s->out.vol.mute); 8053e44607eSKővágó, Zoltán for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { 8063e44607eSKővágó, Zoltán fprintf(stderr, ", vol[%d] %3d", i, s->out.vol.vol[i]); 807f1ae32a1SGerd Hoffmann } 8083e44607eSKővágó, Zoltán fprintf(stderr, "\n"); 8093e44607eSKővágó, Zoltán } 8103e44607eSKővágó, Zoltán audio_set_volume_out(s->out.voice, &s->out.vol); 811f1ae32a1SGerd Hoffmann } 812f1ae32a1SGerd Hoffmann 813f1ae32a1SGerd Hoffmann return ret; 814f1ae32a1SGerd Hoffmann } 815f1ae32a1SGerd Hoffmann 8169a77a0f5SHans de Goede static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, 817f1ae32a1SGerd Hoffmann int request, int value, int index, 818f1ae32a1SGerd Hoffmann int length, uint8_t *data) 819f1ae32a1SGerd Hoffmann { 8200389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 821f1ae32a1SGerd Hoffmann int ret = 0; 822f1ae32a1SGerd Hoffmann 823f1ae32a1SGerd Hoffmann if (s->debug) { 824f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: control transaction: " 825f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 826f1ae32a1SGerd Hoffmann request, value, index, length); 827f1ae32a1SGerd Hoffmann } 828f1ae32a1SGerd Hoffmann 829f1ae32a1SGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 830f1ae32a1SGerd Hoffmann if (ret >= 0) { 8319a77a0f5SHans de Goede return; 832f1ae32a1SGerd Hoffmann } 833f1ae32a1SGerd Hoffmann 834f1ae32a1SGerd Hoffmann switch (request) { 835f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_CUR: 836f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MIN: 837f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MAX: 838f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_RES: 839f1ae32a1SGerd Hoffmann ret = usb_audio_get_control(s, request & 0xff, value, index, 840f1ae32a1SGerd Hoffmann length, data); 841f1ae32a1SGerd Hoffmann if (ret < 0) { 842f1ae32a1SGerd Hoffmann if (s->debug) { 843f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: get control\n"); 844f1ae32a1SGerd Hoffmann } 845f1ae32a1SGerd Hoffmann goto fail; 846f1ae32a1SGerd Hoffmann } 8479a77a0f5SHans de Goede p->actual_length = ret; 848f1ae32a1SGerd Hoffmann break; 849f1ae32a1SGerd Hoffmann 850f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_CUR: 851f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MIN: 852f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MAX: 853f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_RES: 854f1ae32a1SGerd Hoffmann ret = usb_audio_set_control(s, request & 0xff, value, index, 855f1ae32a1SGerd Hoffmann length, data); 856f1ae32a1SGerd Hoffmann if (ret < 0) { 857f1ae32a1SGerd Hoffmann if (s->debug) { 858f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: set control\n"); 859f1ae32a1SGerd Hoffmann } 860f1ae32a1SGerd Hoffmann goto fail; 861f1ae32a1SGerd Hoffmann } 862f1ae32a1SGerd Hoffmann break; 863f1ae32a1SGerd Hoffmann 864f1ae32a1SGerd Hoffmann default: 865f1ae32a1SGerd Hoffmann fail: 866f1ae32a1SGerd Hoffmann if (s->debug) { 867f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed control transaction: " 868f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 869f1ae32a1SGerd Hoffmann request, value, index, length); 870f1ae32a1SGerd Hoffmann } 8719a77a0f5SHans de Goede p->status = USB_RET_STALL; 872f1ae32a1SGerd Hoffmann break; 873f1ae32a1SGerd Hoffmann } 874f1ae32a1SGerd Hoffmann } 875f1ae32a1SGerd Hoffmann 876f1ae32a1SGerd Hoffmann static void usb_audio_set_interface(USBDevice *dev, int iface, 877f1ae32a1SGerd Hoffmann int old, int value) 878f1ae32a1SGerd Hoffmann { 8790389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 880f1ae32a1SGerd Hoffmann 881f1ae32a1SGerd Hoffmann if (iface == 1) { 882f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, value); 883f1ae32a1SGerd Hoffmann } 884f1ae32a1SGerd Hoffmann } 885f1ae32a1SGerd Hoffmann 886f1ae32a1SGerd Hoffmann static void usb_audio_handle_reset(USBDevice *dev) 887f1ae32a1SGerd Hoffmann { 8880389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 889f1ae32a1SGerd Hoffmann 890f1ae32a1SGerd Hoffmann if (s->debug) { 891f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: reset\n"); 892f1ae32a1SGerd Hoffmann } 893f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 894f1ae32a1SGerd Hoffmann } 895f1ae32a1SGerd Hoffmann 8969a77a0f5SHans de Goede static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) 897f1ae32a1SGerd Hoffmann { 898f1ae32a1SGerd Hoffmann if (s->out.altset == ALTSET_OFF) { 8999a77a0f5SHans de Goede p->status = USB_RET_STALL; 9009a77a0f5SHans de Goede return; 901f1ae32a1SGerd Hoffmann } 902f1ae32a1SGerd Hoffmann 9033e44607eSKővágó, Zoltán streambuf_put(&s->out.buf, p, s->out.channels); 9049a77a0f5SHans de Goede if (p->actual_length < p->iov.size && s->debug > 1) { 905f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", 9069a77a0f5SHans de Goede p->iov.size - p->actual_length); 9079a77a0f5SHans de Goede } 908f1ae32a1SGerd Hoffmann } 909f1ae32a1SGerd Hoffmann 9109a77a0f5SHans de Goede static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) 911f1ae32a1SGerd Hoffmann { 912f1ae32a1SGerd Hoffmann USBAudioState *s = (USBAudioState *) dev; 913f1ae32a1SGerd Hoffmann 9149a77a0f5SHans de Goede if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { 9159a77a0f5SHans de Goede usb_audio_handle_dataout(s, p); 9169a77a0f5SHans de Goede return; 917f1ae32a1SGerd Hoffmann } 918f1ae32a1SGerd Hoffmann 9199a77a0f5SHans de Goede p->status = USB_RET_STALL; 9209a77a0f5SHans de Goede if (s->debug) { 921f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed data transaction: " 922f1ae32a1SGerd Hoffmann "pid 0x%x ep 0x%x len 0x%zx\n", 923f1ae32a1SGerd Hoffmann p->pid, p->ep->nr, p->iov.size); 924f1ae32a1SGerd Hoffmann } 925f1ae32a1SGerd Hoffmann } 926f1ae32a1SGerd Hoffmann 927b69c3c21SMarkus Armbruster static void usb_audio_unrealize(USBDevice *dev) 928f1ae32a1SGerd Hoffmann { 9290389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 930f1ae32a1SGerd Hoffmann 931f1ae32a1SGerd Hoffmann if (s->debug) { 932f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: destroy\n"); 933f1ae32a1SGerd Hoffmann } 934f1ae32a1SGerd Hoffmann 935f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 936f1ae32a1SGerd Hoffmann AUD_close_out(&s->card, s->out.voice); 937f1ae32a1SGerd Hoffmann AUD_remove_card(&s->card); 938f1ae32a1SGerd Hoffmann 939f1ae32a1SGerd Hoffmann streambuf_fini(&s->out.buf); 940f1ae32a1SGerd Hoffmann } 941f1ae32a1SGerd Hoffmann 9425450eeaaSGonglei static void usb_audio_realize(USBDevice *dev, Error **errp) 943f1ae32a1SGerd Hoffmann { 9440389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 9453e44607eSKővágó, Zoltán int i; 9463e44607eSKővágó, Zoltán 947*cb94ff5fSMartin Kletzander if (!AUD_register_card(TYPE_USB_AUDIO, &s->card, errp)) { 948*cb94ff5fSMartin Kletzander return; 949*cb94ff5fSMartin Kletzander } 950*cb94ff5fSMartin Kletzander 9513e44607eSKővágó, Zoltán dev->usb_desc = s->multi ? &desc_audio_multi : &desc_audio; 952f1ae32a1SGerd Hoffmann 9539d55d1adSGerd Hoffmann usb_desc_create_serial(dev); 954f1ae32a1SGerd Hoffmann usb_desc_init(dev); 955f1ae32a1SGerd Hoffmann s->dev.opaque = s; 956f1ae32a1SGerd Hoffmann 957f1ae32a1SGerd Hoffmann s->out.altset = ALTSET_OFF; 9583e44607eSKővágó, Zoltán s->out.vol.mute = false; 9593e44607eSKővágó, Zoltán for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { 9603e44607eSKővágó, Zoltán s->out.vol.vol[i] = 240; /* 0 dB */ 9613e44607eSKővágó, Zoltán } 9623e44607eSKővágó, Zoltán 9633e44607eSKővágó, Zoltán usb_audio_reinit(dev, 2); 9643e44607eSKővágó, Zoltán } 9653e44607eSKővágó, Zoltán 9663e44607eSKővágó, Zoltán static void usb_audio_reinit(USBDevice *dev, unsigned channels) 9673e44607eSKővágó, Zoltán { 9683e44607eSKővágó, Zoltán USBAudioState *s = USB_AUDIO(dev); 9693e44607eSKővágó, Zoltán 9703e44607eSKővágó, Zoltán s->out.channels = channels; 9713e44607eSKővágó, Zoltán if (!s->buffer_user) { 9723e44607eSKővágó, Zoltán s->buffer = 32 * USBAUDIO_PACKET_SIZE(s->out.channels); 9733e44607eSKővágó, Zoltán } else { 9743e44607eSKővágó, Zoltán s->buffer = s->buffer_user; 9753e44607eSKővágó, Zoltán } 9763e44607eSKővágó, Zoltán 9773e44607eSKővágó, Zoltán s->out.vol.channels = s->out.channels; 978f1ae32a1SGerd Hoffmann s->out.as.freq = USBAUDIO_SAMPLE_RATE; 9793e44607eSKővágó, Zoltán s->out.as.nchannels = s->out.channels; 98085bc5852SKővágó, Zoltán s->out.as.fmt = AUDIO_FORMAT_S16; 981f1ae32a1SGerd Hoffmann s->out.as.endianness = 0; 9823e44607eSKővágó, Zoltán streambuf_init(&s->out.buf, s->buffer, s->out.channels); 983f1ae32a1SGerd Hoffmann 9840389a0b1SGonglei s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO, 985f1ae32a1SGerd Hoffmann s, output_callback, &s->out.as); 9863e44607eSKővágó, Zoltán audio_set_volume_out(s->out.voice, &s->out.vol); 987f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, 0); 988f1ae32a1SGerd Hoffmann } 989f1ae32a1SGerd Hoffmann 990f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_audio = { 9910389a0b1SGonglei .name = TYPE_USB_AUDIO, 992f1ae32a1SGerd Hoffmann .unmigratable = 1, 993f1ae32a1SGerd Hoffmann }; 994f1ae32a1SGerd Hoffmann 995f1ae32a1SGerd Hoffmann static Property usb_audio_properties[] = { 99688e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(USBAudioState, card), 997f1ae32a1SGerd Hoffmann DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), 9983e44607eSKővágó, Zoltán DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), 9993e44607eSKővágó, Zoltán DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), 1000f1ae32a1SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 1001f1ae32a1SGerd Hoffmann }; 1002f1ae32a1SGerd Hoffmann 1003f1ae32a1SGerd Hoffmann static void usb_audio_class_init(ObjectClass *klass, void *data) 1004f1ae32a1SGerd Hoffmann { 1005f1ae32a1SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 1006f1ae32a1SGerd Hoffmann USBDeviceClass *k = USB_DEVICE_CLASS(klass); 1007f1ae32a1SGerd Hoffmann 1008f1ae32a1SGerd Hoffmann dc->vmsd = &vmstate_usb_audio; 10094f67d30bSMarc-André Lureau device_class_set_props(dc, usb_audio_properties); 1010125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 1011f1ae32a1SGerd Hoffmann k->product_desc = "QEMU USB Audio Interface"; 10125450eeaaSGonglei k->realize = usb_audio_realize; 1013f1ae32a1SGerd Hoffmann k->handle_reset = usb_audio_handle_reset; 1014f1ae32a1SGerd Hoffmann k->handle_control = usb_audio_handle_control; 1015f1ae32a1SGerd Hoffmann k->handle_data = usb_audio_handle_data; 1016c4fe9700SMarc-André Lureau k->unrealize = usb_audio_unrealize; 1017f1ae32a1SGerd Hoffmann k->set_interface = usb_audio_set_interface; 1018f1ae32a1SGerd Hoffmann } 1019f1ae32a1SGerd Hoffmann 10208c43a6f0SAndreas Färber static const TypeInfo usb_audio_info = { 10210389a0b1SGonglei .name = TYPE_USB_AUDIO, 1022f1ae32a1SGerd Hoffmann .parent = TYPE_USB_DEVICE, 1023f1ae32a1SGerd Hoffmann .instance_size = sizeof(USBAudioState), 1024f1ae32a1SGerd Hoffmann .class_init = usb_audio_class_init, 1025f1ae32a1SGerd Hoffmann }; 1026f1ae32a1SGerd Hoffmann 1027f1ae32a1SGerd Hoffmann static void usb_audio_register_types(void) 1028f1ae32a1SGerd Hoffmann { 1029f1ae32a1SGerd Hoffmann type_register_static(&usb_audio_info); 1030f1ae32a1SGerd Hoffmann } 1031f1ae32a1SGerd Hoffmann 1032f1ae32a1SGerd Hoffmann type_init(usb_audio_register_types) 1033