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" 39f1ae32a1SGerd Hoffmann 403e44607eSKővágó, Zoltán static void usb_audio_reinit(USBDevice *dev, unsigned channels); 413e44607eSKővágó, Zoltán 42f1ae32a1SGerd Hoffmann #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ 43f1ae32a1SGerd Hoffmann #define USBAUDIO_PRODUCT_NUM 0x0002 44f1ae32a1SGerd Hoffmann 45f1ae32a1SGerd Hoffmann #define DEV_CONFIG_VALUE 1 /* The one and only */ 46f1ae32a1SGerd Hoffmann 473e44607eSKővágó, Zoltán #define USBAUDIO_MAX_CHANNELS(s) (s->multi ? 8 : 2) 483e44607eSKővágó, Zoltán 49f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AC interfaces */ 50f1ae32a1SGerd Hoffmann #define DST_AC_HEADER 1 51f1ae32a1SGerd Hoffmann #define DST_AC_INPUT_TERMINAL 2 52f1ae32a1SGerd Hoffmann #define DST_AC_OUTPUT_TERMINAL 3 53f1ae32a1SGerd Hoffmann #define DST_AC_FEATURE_UNIT 6 54f1ae32a1SGerd Hoffmann /* Descriptor subtypes for AS interfaces */ 55f1ae32a1SGerd Hoffmann #define DST_AS_GENERAL 1 56f1ae32a1SGerd Hoffmann #define DST_AS_FORMAT_TYPE 2 57f1ae32a1SGerd Hoffmann /* Descriptor subtypes for endpoints */ 58f1ae32a1SGerd Hoffmann #define DST_EP_GENERAL 1 59f1ae32a1SGerd Hoffmann 60f1ae32a1SGerd Hoffmann enum usb_audio_strings { 61f1ae32a1SGerd Hoffmann STRING_NULL, 62f1ae32a1SGerd Hoffmann STRING_MANUFACTURER, 63f1ae32a1SGerd Hoffmann STRING_PRODUCT, 64f1ae32a1SGerd Hoffmann STRING_SERIALNUMBER, 65f1ae32a1SGerd Hoffmann STRING_CONFIG, 66f1ae32a1SGerd Hoffmann STRING_USBAUDIO_CONTROL, 67f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, 68f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, 69f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, 70f1ae32a1SGerd Hoffmann STRING_NULL_STREAM, 71f1ae32a1SGerd Hoffmann STRING_REAL_STREAM, 72f1ae32a1SGerd Hoffmann }; 73f1ae32a1SGerd Hoffmann 74f1ae32a1SGerd Hoffmann static const USBDescStrings usb_audio_stringtable = { 75f1ae32a1SGerd Hoffmann [STRING_MANUFACTURER] = "QEMU", 76f1ae32a1SGerd Hoffmann [STRING_PRODUCT] = "QEMU USB Audio", 77f1ae32a1SGerd Hoffmann [STRING_SERIALNUMBER] = "1", 78f1ae32a1SGerd Hoffmann [STRING_CONFIG] = "Audio Configuration", 79f1ae32a1SGerd Hoffmann [STRING_USBAUDIO_CONTROL] = "Audio Device", 80f1ae32a1SGerd Hoffmann [STRING_INPUT_TERMINAL] = "Audio Output Pipe", 81f1ae32a1SGerd Hoffmann [STRING_FEATURE_UNIT] = "Audio Output Volume Control", 82f1ae32a1SGerd Hoffmann [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", 83f1ae32a1SGerd Hoffmann [STRING_NULL_STREAM] = "Audio Output - Disabled", 84f1ae32a1SGerd Hoffmann [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", 85f1ae32a1SGerd Hoffmann }; 86f1ae32a1SGerd Hoffmann 873e44607eSKővágó, Zoltán /* 883e44607eSKővágó, Zoltán * A USB audio device supports an arbitrary number of alternate 893e44607eSKővágó, Zoltán * interface settings for each interface. Each corresponds to a block 903e44607eSKővágó, Zoltán * diagram of parameterized blocks. This can thus refer to things like 913e44607eSKővágó, Zoltán * number of channels, data rates, or in fact completely different 923e44607eSKővágó, Zoltán * block diagrams. Alternative setting 0 is always the null block diagram, 933e44607eSKővágó, Zoltán * which is used by a disabled device. 943e44607eSKővágó, Zoltán */ 953e44607eSKővágó, Zoltán enum usb_audio_altset { 963e44607eSKővágó, Zoltán ALTSET_OFF = 0x00, /* No endpoint */ 973e44607eSKővágó, Zoltán ALTSET_STEREO = 0x01, /* Single endpoint */ 983e44607eSKővágó, Zoltán ALTSET_51 = 0x02, 993e44607eSKővágó, Zoltán ALTSET_71 = 0x03, 1003e44607eSKővágó, Zoltán }; 1013e44607eSKővágó, Zoltán 1023e44607eSKővágó, Zoltán static unsigned altset_channels[] = { 1033e44607eSKővágó, Zoltán [ALTSET_STEREO] = 2, 1043e44607eSKővágó, Zoltán [ALTSET_51] = 6, 1053e44607eSKővágó, Zoltán [ALTSET_71] = 8, 1063e44607eSKővágó, Zoltán }; 1073e44607eSKővágó, Zoltán 108f1ae32a1SGerd Hoffmann #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) 109f1ae32a1SGerd Hoffmann #define U24(x) U16(x), (((x) >> 16) & 0xff) 110f1ae32a1SGerd Hoffmann #define U32(x) U24(x), (((x) >> 24) & 0xff) 111f1ae32a1SGerd Hoffmann 112f1ae32a1SGerd Hoffmann /* 113f1ae32a1SGerd Hoffmann * A Basic Audio Device uses these specific values 114f1ae32a1SGerd Hoffmann */ 1153e44607eSKővágó, Zoltán #define USBAUDIO_PACKET_SIZE_BASE 96 1163e44607eSKővágó, Zoltán #define USBAUDIO_PACKET_SIZE(channels) (USBAUDIO_PACKET_SIZE_BASE * channels) 117f1ae32a1SGerd Hoffmann #define USBAUDIO_SAMPLE_RATE 48000 118f1ae32a1SGerd Hoffmann #define USBAUDIO_PACKET_INTERVAL 1 119f1ae32a1SGerd Hoffmann 120f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface[] = { 121f1ae32a1SGerd Hoffmann { 122f1ae32a1SGerd Hoffmann .bInterfaceNumber = 0, 123f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 124f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 125f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, 126f1ae32a1SGerd Hoffmann .bInterfaceProtocol = 0x04, 127f1ae32a1SGerd Hoffmann .iInterface = STRING_USBAUDIO_CONTROL, 128f1ae32a1SGerd Hoffmann .ndesc = 4, 129f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 130f1ae32a1SGerd Hoffmann { 131f1ae32a1SGerd Hoffmann /* Headphone Class-Specific AC Interface Header Descriptor */ 132f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 133f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 134f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 135f1ae32a1SGerd Hoffmann DST_AC_HEADER, /* u8 bDescriptorSubtype */ 136f1ae32a1SGerd Hoffmann U16(0x0100), /* u16 bcdADC */ 137f1ae32a1SGerd Hoffmann U16(0x2b), /* u16 wTotalLength */ 138f1ae32a1SGerd Hoffmann 0x01, /* u8 bInCollection */ 139f1ae32a1SGerd Hoffmann 0x01, /* u8 baInterfaceNr */ 140f1ae32a1SGerd Hoffmann } 141f1ae32a1SGerd Hoffmann },{ 142f1ae32a1SGerd Hoffmann /* Generic Stereo Input Terminal ID1 Descriptor */ 143f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 144f1ae32a1SGerd Hoffmann 0x0c, /* u8 bLength */ 145f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 146f1ae32a1SGerd Hoffmann DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ 147f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalID */ 148f1ae32a1SGerd Hoffmann U16(0x0101), /* u16 wTerminalType */ 149f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 1503e44607eSKővágó, Zoltán 0x02, /* u8 bNrChannels */ 151f1ae32a1SGerd Hoffmann U16(0x0003), /* u16 wChannelConfig */ 152f1ae32a1SGerd Hoffmann 0x00, /* u8 iChannelNames */ 153f1ae32a1SGerd Hoffmann STRING_INPUT_TERMINAL, /* u8 iTerminal */ 154f1ae32a1SGerd Hoffmann } 155f1ae32a1SGerd Hoffmann },{ 156f1ae32a1SGerd Hoffmann /* Generic Stereo Feature Unit ID2 Descriptor */ 157f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 158f1ae32a1SGerd Hoffmann 0x0d, /* u8 bLength */ 159f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 160f1ae32a1SGerd Hoffmann DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ 161f1ae32a1SGerd Hoffmann 0x02, /* u8 bUnitID */ 162f1ae32a1SGerd Hoffmann 0x01, /* u8 bSourceID */ 163f1ae32a1SGerd Hoffmann 0x02, /* u8 bControlSize */ 164f1ae32a1SGerd Hoffmann U16(0x0001), /* u16 bmaControls(0) */ 165f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(1) */ 166f1ae32a1SGerd Hoffmann U16(0x0002), /* u16 bmaControls(2) */ 167f1ae32a1SGerd Hoffmann STRING_FEATURE_UNIT, /* u8 iFeature */ 168f1ae32a1SGerd Hoffmann } 169f1ae32a1SGerd Hoffmann },{ 170f1ae32a1SGerd Hoffmann /* Headphone Ouptut Terminal ID3 Descriptor */ 171f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 172f1ae32a1SGerd Hoffmann 0x09, /* u8 bLength */ 173f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 174f1ae32a1SGerd Hoffmann DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ 175f1ae32a1SGerd Hoffmann 0x03, /* u8 bUnitID */ 176f1ae32a1SGerd Hoffmann U16(0x0301), /* u16 wTerminalType (SPK) */ 177f1ae32a1SGerd Hoffmann 0x00, /* u8 bAssocTerminal */ 178f1ae32a1SGerd Hoffmann 0x02, /* u8 bSourceID */ 179f1ae32a1SGerd Hoffmann STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ 180f1ae32a1SGerd Hoffmann } 181f1ae32a1SGerd Hoffmann } 182f1ae32a1SGerd Hoffmann }, 183f1ae32a1SGerd Hoffmann },{ 184f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 1853e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_OFF, 186f1ae32a1SGerd Hoffmann .bNumEndpoints = 0, 187f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 188f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 189f1ae32a1SGerd Hoffmann .iInterface = STRING_NULL_STREAM, 190f1ae32a1SGerd Hoffmann },{ 191f1ae32a1SGerd Hoffmann .bInterfaceNumber = 1, 1923e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_STEREO, 193f1ae32a1SGerd Hoffmann .bNumEndpoints = 1, 194f1ae32a1SGerd Hoffmann .bInterfaceClass = USB_CLASS_AUDIO, 195f1ae32a1SGerd Hoffmann .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 196f1ae32a1SGerd Hoffmann .iInterface = STRING_REAL_STREAM, 197f1ae32a1SGerd Hoffmann .ndesc = 2, 198f1ae32a1SGerd Hoffmann .descs = (USBDescOther[]) { 199f1ae32a1SGerd Hoffmann { 200f1ae32a1SGerd Hoffmann /* Headphone Class-specific AS General Interface Descriptor */ 201f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 202f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 203f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 204f1ae32a1SGerd Hoffmann DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 205f1ae32a1SGerd Hoffmann 0x01, /* u8 bTerminalLink */ 206f1ae32a1SGerd Hoffmann 0x00, /* u8 bDelay */ 207f1ae32a1SGerd Hoffmann 0x01, 0x00, /* u16 wFormatTag */ 208f1ae32a1SGerd Hoffmann } 209f1ae32a1SGerd Hoffmann },{ 210f1ae32a1SGerd Hoffmann /* Headphone Type I Format Type Descriptor */ 211f1ae32a1SGerd Hoffmann .data = (uint8_t[]) { 212f1ae32a1SGerd Hoffmann 0x0b, /* u8 bLength */ 213f1ae32a1SGerd Hoffmann USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 214f1ae32a1SGerd Hoffmann DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 215f1ae32a1SGerd Hoffmann 0x01, /* u8 bFormatType */ 216f1ae32a1SGerd Hoffmann 0x02, /* u8 bNrChannels */ 217f1ae32a1SGerd Hoffmann 0x02, /* u8 bSubFrameSize */ 218f1ae32a1SGerd Hoffmann 0x10, /* u8 bBitResolution */ 219f1ae32a1SGerd Hoffmann 0x01, /* u8 bSamFreqType */ 220f1ae32a1SGerd Hoffmann U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 221f1ae32a1SGerd Hoffmann } 222f1ae32a1SGerd Hoffmann } 223f1ae32a1SGerd Hoffmann }, 224f1ae32a1SGerd Hoffmann .eps = (USBDescEndpoint[]) { 225f1ae32a1SGerd Hoffmann { 226f1ae32a1SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | 0x01, 227f1ae32a1SGerd Hoffmann .bmAttributes = 0x0d, 2283e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), 229f1ae32a1SGerd Hoffmann .bInterval = 1, 230f1ae32a1SGerd Hoffmann .is_audio = 1, 231f1ae32a1SGerd Hoffmann /* Stereo Headphone Class-specific 232f1ae32a1SGerd Hoffmann AS Audio Data Endpoint Descriptor */ 233f1ae32a1SGerd Hoffmann .extra = (uint8_t[]) { 234f1ae32a1SGerd Hoffmann 0x07, /* u8 bLength */ 235f1ae32a1SGerd Hoffmann USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 236f1ae32a1SGerd Hoffmann DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 237f1ae32a1SGerd Hoffmann 0x00, /* u8 bmAttributes */ 238f1ae32a1SGerd Hoffmann 0x00, /* u8 bLockDelayUnits */ 239f1ae32a1SGerd Hoffmann U16(0x0000), /* u16 wLockDelay */ 240f1ae32a1SGerd Hoffmann }, 241f1ae32a1SGerd Hoffmann }, 242f1ae32a1SGerd Hoffmann } 243f1ae32a1SGerd Hoffmann } 244f1ae32a1SGerd Hoffmann }; 245f1ae32a1SGerd Hoffmann 246f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device = { 2472bbd086cSGerd Hoffmann .bcdUSB = 0x0100, 248f1ae32a1SGerd Hoffmann .bMaxPacketSize0 = 64, 249f1ae32a1SGerd Hoffmann .bNumConfigurations = 1, 250f1ae32a1SGerd Hoffmann .confs = (USBDescConfig[]) { 251f1ae32a1SGerd Hoffmann { 252f1ae32a1SGerd Hoffmann .bNumInterfaces = 2, 253f1ae32a1SGerd Hoffmann .bConfigurationValue = DEV_CONFIG_VALUE, 254f1ae32a1SGerd Hoffmann .iConfiguration = STRING_CONFIG, 255bd93976aSPantelis Koukousoulas .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, 256f1ae32a1SGerd Hoffmann .bMaxPower = 0x32, 257f1ae32a1SGerd Hoffmann .nif = ARRAY_SIZE(desc_iface), 258f1ae32a1SGerd Hoffmann .ifs = desc_iface, 259f1ae32a1SGerd Hoffmann }, 260f1ae32a1SGerd Hoffmann }, 261f1ae32a1SGerd Hoffmann }; 262f1ae32a1SGerd Hoffmann 263f1ae32a1SGerd Hoffmann static const USBDesc desc_audio = { 264f1ae32a1SGerd Hoffmann .id = { 265f1ae32a1SGerd Hoffmann .idVendor = USBAUDIO_VENDOR_NUM, 266f1ae32a1SGerd Hoffmann .idProduct = USBAUDIO_PRODUCT_NUM, 267f1ae32a1SGerd Hoffmann .bcdDevice = 0, 268f1ae32a1SGerd Hoffmann .iManufacturer = STRING_MANUFACTURER, 269f1ae32a1SGerd Hoffmann .iProduct = STRING_PRODUCT, 270f1ae32a1SGerd Hoffmann .iSerialNumber = STRING_SERIALNUMBER, 271f1ae32a1SGerd Hoffmann }, 272f1ae32a1SGerd Hoffmann .full = &desc_device, 273f1ae32a1SGerd Hoffmann .str = usb_audio_stringtable, 274f1ae32a1SGerd Hoffmann }; 275f1ae32a1SGerd Hoffmann 2763e44607eSKővágó, Zoltán /* multi channel compatible desc */ 2773e44607eSKővágó, Zoltán 2783e44607eSKővágó, Zoltán static const USBDescIface desc_iface_multi[] = { 2793e44607eSKővágó, Zoltán { 2803e44607eSKővágó, Zoltán .bInterfaceNumber = 0, 2813e44607eSKővágó, Zoltán .bNumEndpoints = 0, 2823e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 2833e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, 2843e44607eSKővágó, Zoltán .bInterfaceProtocol = 0x04, 2853e44607eSKővágó, Zoltán .iInterface = STRING_USBAUDIO_CONTROL, 2863e44607eSKővágó, Zoltán .ndesc = 4, 2873e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 2883e44607eSKővágó, Zoltán { 2893e44607eSKővágó, Zoltán /* Headphone Class-Specific AC Interface Header Descriptor */ 2903e44607eSKővágó, Zoltán .data = (uint8_t[]) { 2913e44607eSKővágó, Zoltán 0x09, /* u8 bLength */ 2923e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 2933e44607eSKővágó, Zoltán DST_AC_HEADER, /* u8 bDescriptorSubtype */ 2943e44607eSKővágó, Zoltán U16(0x0100), /* u16 bcdADC */ 2953e44607eSKővágó, Zoltán U16(0x38), /* u16 wTotalLength */ 2963e44607eSKővágó, Zoltán 0x01, /* u8 bInCollection */ 2973e44607eSKővágó, Zoltán 0x01, /* u8 baInterfaceNr */ 2983e44607eSKővágó, Zoltán } 2993e44607eSKővágó, Zoltán },{ 3003e44607eSKővágó, Zoltán /* Generic Stereo Input Terminal ID1 Descriptor */ 3013e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3023e44607eSKővágó, Zoltán 0x0c, /* u8 bLength */ 3033e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3043e44607eSKővágó, Zoltán DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ 3053e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalID */ 3063e44607eSKővágó, Zoltán U16(0x0101), /* u16 wTerminalType */ 3073e44607eSKővágó, Zoltán 0x00, /* u8 bAssocTerminal */ 3083e44607eSKővágó, Zoltán 0x08, /* u8 bNrChannels */ 3093e44607eSKővágó, Zoltán U16(0x063f), /* u16 wChannelConfig */ 3103e44607eSKővágó, Zoltán 0x00, /* u8 iChannelNames */ 3113e44607eSKővágó, Zoltán STRING_INPUT_TERMINAL, /* u8 iTerminal */ 3123e44607eSKővágó, Zoltán } 3133e44607eSKővágó, Zoltán },{ 3143e44607eSKővágó, Zoltán /* Generic Stereo Feature Unit ID2 Descriptor */ 3153e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3163e44607eSKővágó, Zoltán 0x19, /* u8 bLength */ 3173e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3183e44607eSKővágó, Zoltán DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ 3193e44607eSKővágó, Zoltán 0x02, /* u8 bUnitID */ 3203e44607eSKővágó, Zoltán 0x01, /* u8 bSourceID */ 3213e44607eSKővágó, Zoltán 0x02, /* u8 bControlSize */ 3223e44607eSKővágó, Zoltán U16(0x0001), /* u16 bmaControls(0) */ 3233e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(1) */ 3243e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(2) */ 3253e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(3) */ 3263e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(4) */ 3273e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(5) */ 3283e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(6) */ 3293e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(7) */ 3303e44607eSKővágó, Zoltán U16(0x0002), /* u16 bmaControls(8) */ 3313e44607eSKővágó, Zoltán STRING_FEATURE_UNIT, /* u8 iFeature */ 3323e44607eSKővágó, Zoltán } 3333e44607eSKővágó, Zoltán },{ 3343e44607eSKővágó, Zoltán /* Headphone Ouptut Terminal ID3 Descriptor */ 3353e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3363e44607eSKővágó, Zoltán 0x09, /* u8 bLength */ 3373e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3383e44607eSKővágó, Zoltán DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ 3393e44607eSKővágó, Zoltán 0x03, /* u8 bUnitID */ 3403e44607eSKővágó, Zoltán U16(0x0301), /* u16 wTerminalType (SPK) */ 3413e44607eSKővágó, Zoltán 0x00, /* u8 bAssocTerminal */ 3423e44607eSKővágó, Zoltán 0x02, /* u8 bSourceID */ 3433e44607eSKővágó, Zoltán STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ 3443e44607eSKővágó, Zoltán } 3453e44607eSKővágó, Zoltán } 3463e44607eSKővágó, Zoltán }, 3473e44607eSKővágó, Zoltán },{ 3483e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 3493e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_OFF, 3503e44607eSKővágó, Zoltán .bNumEndpoints = 0, 3513e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 3523e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 3533e44607eSKővágó, Zoltán .iInterface = STRING_NULL_STREAM, 3543e44607eSKővágó, Zoltán },{ 3553e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 3563e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_STEREO, 3573e44607eSKővágó, Zoltán .bNumEndpoints = 1, 3583e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 3593e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 3603e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 3613e44607eSKővágó, Zoltán .ndesc = 2, 3623e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 3633e44607eSKővágó, Zoltán { 3643e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 3653e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3663e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 3673e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3683e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 3693e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 3703e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 3713e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 3723e44607eSKővágó, Zoltán } 3733e44607eSKővágó, Zoltán },{ 3743e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 3753e44607eSKővágó, Zoltán .data = (uint8_t[]) { 3763e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 3773e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 3783e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 3793e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 3803e44607eSKővágó, Zoltán 0x02, /* u8 bNrChannels */ 3813e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 3823e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 3833e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 3843e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 3853e44607eSKővágó, Zoltán } 3863e44607eSKővágó, Zoltán } 3873e44607eSKővágó, Zoltán }, 3883e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 3893e44607eSKővágó, Zoltán { 3903e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 3913e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 3923e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(2), 3933e44607eSKővágó, Zoltán .bInterval = 1, 3943e44607eSKővágó, Zoltán .is_audio = 1, 3953e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 3963e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 3973e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 3983e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 3993e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 4003e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 4013e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 4023e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 4033e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 4043e44607eSKővágó, Zoltán }, 4053e44607eSKővágó, Zoltán }, 4063e44607eSKővágó, Zoltán } 4073e44607eSKővágó, Zoltán },{ 4083e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 4093e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_51, 4103e44607eSKővágó, Zoltán .bNumEndpoints = 1, 4113e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 4123e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 4133e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 4143e44607eSKővágó, Zoltán .ndesc = 2, 4153e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 4163e44607eSKővágó, Zoltán { 4173e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 4183e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4193e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4203e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4213e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 4223e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 4233e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 4243e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 4253e44607eSKővágó, Zoltán } 4263e44607eSKővágó, Zoltán },{ 4273e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 4283e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4293e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 4303e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4313e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 4323e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 4333e44607eSKővágó, Zoltán 0x06, /* u8 bNrChannels */ 4343e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 4353e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 4363e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 4373e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 4383e44607eSKővágó, Zoltán } 4393e44607eSKővágó, Zoltán } 4403e44607eSKővágó, Zoltán }, 4413e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 4423e44607eSKővágó, Zoltán { 4433e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 4443e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 4453e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(6), 4463e44607eSKővágó, Zoltán .bInterval = 1, 4473e44607eSKővágó, Zoltán .is_audio = 1, 4483e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 4493e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 4503e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 4513e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4523e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 4533e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 4543e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 4553e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 4563e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 4573e44607eSKővágó, Zoltán }, 4583e44607eSKővágó, Zoltán }, 4593e44607eSKővágó, Zoltán } 4603e44607eSKővágó, Zoltán },{ 4613e44607eSKővágó, Zoltán .bInterfaceNumber = 1, 4623e44607eSKővágó, Zoltán .bAlternateSetting = ALTSET_71, 4633e44607eSKővágó, Zoltán .bNumEndpoints = 1, 4643e44607eSKővágó, Zoltán .bInterfaceClass = USB_CLASS_AUDIO, 4653e44607eSKővágó, Zoltán .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 4663e44607eSKővágó, Zoltán .iInterface = STRING_REAL_STREAM, 4673e44607eSKővágó, Zoltán .ndesc = 2, 4683e44607eSKővágó, Zoltán .descs = (USBDescOther[]) { 4693e44607eSKővágó, Zoltán { 4703e44607eSKővágó, Zoltán /* Headphone Class-specific AS General Interface Descriptor */ 4713e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4723e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 4733e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4743e44607eSKővágó, Zoltán DST_AS_GENERAL, /* u8 bDescriptorSubtype */ 4753e44607eSKővágó, Zoltán 0x01, /* u8 bTerminalLink */ 4763e44607eSKővágó, Zoltán 0x00, /* u8 bDelay */ 4773e44607eSKővágó, Zoltán 0x01, 0x00, /* u16 wFormatTag */ 4783e44607eSKővágó, Zoltán } 4793e44607eSKővágó, Zoltán },{ 4803e44607eSKővágó, Zoltán /* Headphone Type I Format Type Descriptor */ 4813e44607eSKővágó, Zoltán .data = (uint8_t[]) { 4823e44607eSKővágó, Zoltán 0x0b, /* u8 bLength */ 4833e44607eSKővágó, Zoltán USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ 4843e44607eSKővágó, Zoltán DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ 4853e44607eSKővágó, Zoltán 0x01, /* u8 bFormatType */ 4863e44607eSKővágó, Zoltán 0x08, /* u8 bNrChannels */ 4873e44607eSKővágó, Zoltán 0x02, /* u8 bSubFrameSize */ 4883e44607eSKővágó, Zoltán 0x10, /* u8 bBitResolution */ 4893e44607eSKővágó, Zoltán 0x01, /* u8 bSamFreqType */ 4903e44607eSKővágó, Zoltán U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ 4913e44607eSKővágó, Zoltán } 4923e44607eSKővágó, Zoltán } 4933e44607eSKővágó, Zoltán }, 4943e44607eSKővágó, Zoltán .eps = (USBDescEndpoint[]) { 4953e44607eSKővágó, Zoltán { 4963e44607eSKővágó, Zoltán .bEndpointAddress = USB_DIR_OUT | 0x01, 4973e44607eSKővágó, Zoltán .bmAttributes = 0x0d, 4983e44607eSKővágó, Zoltán .wMaxPacketSize = USBAUDIO_PACKET_SIZE(8), 4993e44607eSKővágó, Zoltán .bInterval = 1, 5003e44607eSKővágó, Zoltán .is_audio = 1, 5013e44607eSKővágó, Zoltán /* Stereo Headphone Class-specific 5023e44607eSKővágó, Zoltán AS Audio Data Endpoint Descriptor */ 5033e44607eSKővágó, Zoltán .extra = (uint8_t[]) { 5043e44607eSKővágó, Zoltán 0x07, /* u8 bLength */ 5053e44607eSKővágó, Zoltán USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ 5063e44607eSKővágó, Zoltán DST_EP_GENERAL, /* u8 bDescriptorSubtype */ 5073e44607eSKővágó, Zoltán 0x00, /* u8 bmAttributes */ 5083e44607eSKővágó, Zoltán 0x00, /* u8 bLockDelayUnits */ 5093e44607eSKővágó, Zoltán U16(0x0000), /* u16 wLockDelay */ 5103e44607eSKővágó, Zoltán }, 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 static const USBDescDevice desc_device_multi = { 5173e44607eSKővágó, Zoltán .bcdUSB = 0x0100, 5183e44607eSKővágó, Zoltán .bMaxPacketSize0 = 64, 5193e44607eSKővágó, Zoltán .bNumConfigurations = 1, 5203e44607eSKővágó, Zoltán .confs = (USBDescConfig[]) { 5213e44607eSKővágó, Zoltán { 5223e44607eSKővágó, Zoltán .bNumInterfaces = 2, 5233e44607eSKővágó, Zoltán .bConfigurationValue = DEV_CONFIG_VALUE, 5243e44607eSKővágó, Zoltán .iConfiguration = STRING_CONFIG, 5253e44607eSKővágó, Zoltán .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, 5263e44607eSKővágó, Zoltán .bMaxPower = 0x32, 5273e44607eSKővágó, Zoltán .nif = ARRAY_SIZE(desc_iface_multi), 5283e44607eSKővágó, Zoltán .ifs = desc_iface_multi, 5293e44607eSKővágó, Zoltán } 5303e44607eSKővágó, Zoltán }, 5313e44607eSKővágó, Zoltán }; 5323e44607eSKővágó, Zoltán 5333e44607eSKővágó, Zoltán static const USBDesc desc_audio_multi = { 5343e44607eSKővágó, Zoltán .id = { 5353e44607eSKővágó, Zoltán .idVendor = USBAUDIO_VENDOR_NUM, 5363e44607eSKővágó, Zoltán .idProduct = USBAUDIO_PRODUCT_NUM, 5373e44607eSKővágó, Zoltán .bcdDevice = 0, 5383e44607eSKővágó, Zoltán .iManufacturer = STRING_MANUFACTURER, 5393e44607eSKővágó, Zoltán .iProduct = STRING_PRODUCT, 5403e44607eSKővágó, Zoltán .iSerialNumber = STRING_SERIALNUMBER, 5413e44607eSKővágó, Zoltán }, 5423e44607eSKővágó, Zoltán .full = &desc_device_multi, 5433e44607eSKővágó, Zoltán .str = usb_audio_stringtable, 544f1ae32a1SGerd Hoffmann }; 545f1ae32a1SGerd Hoffmann 546f1ae32a1SGerd Hoffmann /* 547f1ae32a1SGerd Hoffmann * Class-specific control requests 548f1ae32a1SGerd Hoffmann */ 549f1ae32a1SGerd Hoffmann #define CR_SET_CUR 0x01 550f1ae32a1SGerd Hoffmann #define CR_GET_CUR 0x81 551f1ae32a1SGerd Hoffmann #define CR_SET_MIN 0x02 552f1ae32a1SGerd Hoffmann #define CR_GET_MIN 0x82 553f1ae32a1SGerd Hoffmann #define CR_SET_MAX 0x03 554f1ae32a1SGerd Hoffmann #define CR_GET_MAX 0x83 555f1ae32a1SGerd Hoffmann #define CR_SET_RES 0x04 556f1ae32a1SGerd Hoffmann #define CR_GET_RES 0x84 557f1ae32a1SGerd Hoffmann #define CR_SET_MEM 0x05 558f1ae32a1SGerd Hoffmann #define CR_GET_MEM 0x85 559f1ae32a1SGerd Hoffmann #define CR_GET_STAT 0xff 560f1ae32a1SGerd Hoffmann 561f1ae32a1SGerd Hoffmann /* 562f1ae32a1SGerd Hoffmann * Feature Unit Control Selectors 563f1ae32a1SGerd Hoffmann */ 564f1ae32a1SGerd Hoffmann #define MUTE_CONTROL 0x01 565f1ae32a1SGerd Hoffmann #define VOLUME_CONTROL 0x02 566f1ae32a1SGerd Hoffmann #define BASS_CONTROL 0x03 567f1ae32a1SGerd Hoffmann #define MID_CONTROL 0x04 568f1ae32a1SGerd Hoffmann #define TREBLE_CONTROL 0x05 569f1ae32a1SGerd Hoffmann #define GRAPHIC_EQUALIZER_CONTROL 0x06 570f1ae32a1SGerd Hoffmann #define AUTOMATIC_GAIN_CONTROL 0x07 571f1ae32a1SGerd Hoffmann #define DELAY_CONTROL 0x08 572f1ae32a1SGerd Hoffmann #define BASS_BOOST_CONTROL 0x09 573f1ae32a1SGerd Hoffmann #define LOUDNESS_CONTROL 0x0a 574f1ae32a1SGerd Hoffmann 575f1ae32a1SGerd Hoffmann /* 576f1ae32a1SGerd Hoffmann * buffering 577f1ae32a1SGerd Hoffmann */ 578f1ae32a1SGerd Hoffmann 579f1ae32a1SGerd Hoffmann struct streambuf { 580f1ae32a1SGerd Hoffmann uint8_t *data; 581670777a9SKővágó, Zoltán size_t size; 582670777a9SKővágó, Zoltán uint64_t prod; 583670777a9SKővágó, Zoltán uint64_t cons; 584f1ae32a1SGerd Hoffmann }; 585f1ae32a1SGerd Hoffmann 5863e44607eSKővágó, Zoltán static void streambuf_init(struct streambuf *buf, uint32_t size, 5873e44607eSKővágó, Zoltán uint32_t channels) 588f1ae32a1SGerd Hoffmann { 589f1ae32a1SGerd Hoffmann g_free(buf->data); 5903e44607eSKővágó, Zoltán buf->size = size - (size % USBAUDIO_PACKET_SIZE(channels)); 591f1ae32a1SGerd Hoffmann buf->data = g_malloc(buf->size); 592f1ae32a1SGerd Hoffmann buf->prod = 0; 593f1ae32a1SGerd Hoffmann buf->cons = 0; 594f1ae32a1SGerd Hoffmann } 595f1ae32a1SGerd Hoffmann 596f1ae32a1SGerd Hoffmann static void streambuf_fini(struct streambuf *buf) 597f1ae32a1SGerd Hoffmann { 598f1ae32a1SGerd Hoffmann g_free(buf->data); 599f1ae32a1SGerd Hoffmann buf->data = NULL; 600f1ae32a1SGerd Hoffmann } 601f1ae32a1SGerd Hoffmann 6023e44607eSKővágó, Zoltán static int streambuf_put(struct streambuf *buf, USBPacket *p, uint32_t channels) 603f1ae32a1SGerd Hoffmann { 604670777a9SKővágó, Zoltán int64_t free = buf->size - (buf->prod - buf->cons); 605f1ae32a1SGerd Hoffmann 6063e44607eSKővágó, Zoltán if (free < USBAUDIO_PACKET_SIZE(channels)) { 607f1ae32a1SGerd Hoffmann return 0; 608f1ae32a1SGerd Hoffmann } 6093e44607eSKővágó, Zoltán if (p->iov.size != USBAUDIO_PACKET_SIZE(channels)) { 610a7fde1c1SGerd Hoffmann return 0; 611a7fde1c1SGerd Hoffmann } 6122c6a740fSKővágó, Zoltán 613670777a9SKővágó, Zoltán /* can happen if prod overflows */ 614670777a9SKővágó, Zoltán assert(buf->prod % USBAUDIO_PACKET_SIZE(channels) == 0); 615f1ae32a1SGerd Hoffmann usb_packet_copy(p, buf->data + (buf->prod % buf->size), 6163e44607eSKővágó, Zoltán USBAUDIO_PACKET_SIZE(channels)); 6173e44607eSKővágó, Zoltán buf->prod += USBAUDIO_PACKET_SIZE(channels); 6183e44607eSKővágó, Zoltán return USBAUDIO_PACKET_SIZE(channels); 619f1ae32a1SGerd Hoffmann } 620f1ae32a1SGerd Hoffmann 6212c6a740fSKővágó, Zoltán static uint8_t *streambuf_get(struct streambuf *buf, size_t *len) 622f1ae32a1SGerd Hoffmann { 623670777a9SKővágó, Zoltán int64_t used = buf->prod - buf->cons; 624f1ae32a1SGerd Hoffmann uint8_t *data; 625f1ae32a1SGerd Hoffmann 626670777a9SKővágó, Zoltán if (used <= 0) { 6272c6a740fSKővágó, Zoltán *len = 0; 628f1ae32a1SGerd Hoffmann return NULL; 629f1ae32a1SGerd Hoffmann } 630f1ae32a1SGerd Hoffmann data = buf->data + (buf->cons % buf->size); 6312c6a740fSKővágó, Zoltán *len = MIN(buf->prod - buf->cons, 6322c6a740fSKővágó, Zoltán buf->size - (buf->cons % buf->size)); 633f1ae32a1SGerd Hoffmann return data; 634f1ae32a1SGerd Hoffmann } 635f1ae32a1SGerd Hoffmann 636f1ae32a1SGerd Hoffmann typedef struct USBAudioState { 637f1ae32a1SGerd Hoffmann /* qemu interfaces */ 638f1ae32a1SGerd Hoffmann USBDevice dev; 639f1ae32a1SGerd Hoffmann QEMUSoundCard card; 640f1ae32a1SGerd Hoffmann 641f1ae32a1SGerd Hoffmann /* state */ 642f1ae32a1SGerd Hoffmann struct { 643f1ae32a1SGerd Hoffmann enum usb_audio_altset altset; 644f1ae32a1SGerd Hoffmann struct audsettings as; 645f1ae32a1SGerd Hoffmann SWVoiceOut *voice; 6463e44607eSKővágó, Zoltán Volume vol; 647f1ae32a1SGerd Hoffmann struct streambuf buf; 6483e44607eSKővágó, Zoltán uint32_t channels; 649f1ae32a1SGerd Hoffmann } out; 650f1ae32a1SGerd Hoffmann 651f1ae32a1SGerd Hoffmann /* properties */ 652f1ae32a1SGerd Hoffmann uint32_t debug; 6533e44607eSKővágó, Zoltán uint32_t buffer_user, buffer; 6543e44607eSKővágó, Zoltán bool multi; 655f1ae32a1SGerd Hoffmann } USBAudioState; 656f1ae32a1SGerd Hoffmann 6570389a0b1SGonglei #define TYPE_USB_AUDIO "usb-audio" 6580389a0b1SGonglei #define USB_AUDIO(obj) OBJECT_CHECK(USBAudioState, (obj), TYPE_USB_AUDIO) 6590389a0b1SGonglei 660f1ae32a1SGerd Hoffmann static void output_callback(void *opaque, int avail) 661f1ae32a1SGerd Hoffmann { 662f1ae32a1SGerd Hoffmann USBAudioState *s = opaque; 663f1ae32a1SGerd Hoffmann uint8_t *data; 664f1ae32a1SGerd Hoffmann 6652c6a740fSKővágó, Zoltán while (avail) { 6662c6a740fSKővágó, Zoltán size_t written, len; 6672c6a740fSKővágó, Zoltán 6682c6a740fSKővágó, Zoltán data = streambuf_get(&s->out.buf, &len); 669d0657b2aSGonglei if (!data) { 670f1ae32a1SGerd Hoffmann return; 671f1ae32a1SGerd Hoffmann } 6722c6a740fSKővágó, Zoltán 6732c6a740fSKővágó, Zoltán written = AUD_write(s->out.voice, data, len); 6742c6a740fSKővágó, Zoltán avail -= written; 6752c6a740fSKővágó, Zoltán s->out.buf.cons += written; 6762c6a740fSKővágó, Zoltán 6772c6a740fSKővágó, Zoltán if (written < len) { 6782c6a740fSKővágó, Zoltán return; 6792c6a740fSKővágó, Zoltán } 680f1ae32a1SGerd Hoffmann } 681f1ae32a1SGerd Hoffmann } 682f1ae32a1SGerd Hoffmann 683f1ae32a1SGerd Hoffmann static int usb_audio_set_output_altset(USBAudioState *s, int altset) 684f1ae32a1SGerd Hoffmann { 685f1ae32a1SGerd Hoffmann switch (altset) { 686f1ae32a1SGerd Hoffmann case ALTSET_OFF: 687f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, false); 688f1ae32a1SGerd Hoffmann break; 6893e44607eSKővágó, Zoltán case ALTSET_STEREO: 6903e44607eSKővágó, Zoltán case ALTSET_51: 6913e44607eSKővágó, Zoltán case ALTSET_71: 6923e44607eSKővágó, Zoltán if (s->out.channels != altset_channels[altset]) { 6933e44607eSKővágó, Zoltán usb_audio_reinit(USB_DEVICE(s), altset_channels[altset]); 6943e44607eSKővágó, Zoltán } 6953e44607eSKővágó, Zoltán streambuf_init(&s->out.buf, s->buffer, s->out.channels); 696f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, true); 697f1ae32a1SGerd Hoffmann break; 698f1ae32a1SGerd Hoffmann default: 699f1ae32a1SGerd Hoffmann return -1; 700f1ae32a1SGerd Hoffmann } 701f1ae32a1SGerd Hoffmann 702f1ae32a1SGerd Hoffmann if (s->debug) { 703f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: set interface %d\n", altset); 704f1ae32a1SGerd Hoffmann } 705f1ae32a1SGerd Hoffmann s->out.altset = altset; 706f1ae32a1SGerd Hoffmann return 0; 707f1ae32a1SGerd Hoffmann } 708f1ae32a1SGerd Hoffmann 709f1ae32a1SGerd Hoffmann /* 710f1ae32a1SGerd Hoffmann * Note: we arbitrarily map the volume control range onto -inf..+8 dB 711f1ae32a1SGerd Hoffmann */ 712f1ae32a1SGerd Hoffmann #define ATTRIB_ID(cs, attrib, idif) \ 713f1ae32a1SGerd Hoffmann (((cs) << 24) | ((attrib) << 16) | (idif)) 714f1ae32a1SGerd Hoffmann 715f1ae32a1SGerd Hoffmann static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, 716f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 717f1ae32a1SGerd Hoffmann int length, uint8_t *data) 718f1ae32a1SGerd Hoffmann { 719f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 720f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 721f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 722f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 723f1ae32a1SGerd Hoffmann 724f1ae32a1SGerd Hoffmann switch (aid) { 725f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): 7263e44607eSKővágó, Zoltán data[0] = s->out.vol.mute; 727f1ae32a1SGerd Hoffmann ret = 1; 728f1ae32a1SGerd Hoffmann break; 729f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): 7303e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 7313e44607eSKővágó, Zoltán uint16_t vol = (s->out.vol.vol[cn] * 0x8800 + 127) / 255 + 0x8000; 732f1ae32a1SGerd Hoffmann data[0] = vol; 733f1ae32a1SGerd Hoffmann data[1] = vol >> 8; 734f1ae32a1SGerd Hoffmann ret = 2; 735f1ae32a1SGerd Hoffmann } 736f1ae32a1SGerd Hoffmann break; 737f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): 7383e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 739f1ae32a1SGerd Hoffmann data[0] = 0x01; 740f1ae32a1SGerd Hoffmann data[1] = 0x80; 741f1ae32a1SGerd Hoffmann ret = 2; 742f1ae32a1SGerd Hoffmann } 743f1ae32a1SGerd Hoffmann break; 744f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): 7453e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 746f1ae32a1SGerd Hoffmann data[0] = 0x00; 747f1ae32a1SGerd Hoffmann data[1] = 0x08; 748f1ae32a1SGerd Hoffmann ret = 2; 749f1ae32a1SGerd Hoffmann } 750f1ae32a1SGerd Hoffmann break; 751f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): 7523e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 753f1ae32a1SGerd Hoffmann data[0] = 0x88; 754f1ae32a1SGerd Hoffmann data[1] = 0x00; 755f1ae32a1SGerd Hoffmann ret = 2; 756f1ae32a1SGerd Hoffmann } 757f1ae32a1SGerd Hoffmann break; 758f1ae32a1SGerd Hoffmann } 759f1ae32a1SGerd Hoffmann 760f1ae32a1SGerd Hoffmann return ret; 761f1ae32a1SGerd Hoffmann } 762f1ae32a1SGerd Hoffmann static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, 763f1ae32a1SGerd Hoffmann uint16_t cscn, uint16_t idif, 764f1ae32a1SGerd Hoffmann int length, uint8_t *data) 765f1ae32a1SGerd Hoffmann { 766f1ae32a1SGerd Hoffmann uint8_t cs = cscn >> 8; 767f1ae32a1SGerd Hoffmann uint8_t cn = cscn - 1; /* -1 for the non-present master control */ 768f1ae32a1SGerd Hoffmann uint32_t aid = ATTRIB_ID(cs, attrib, idif); 769f1ae32a1SGerd Hoffmann int ret = USB_RET_STALL; 770f1ae32a1SGerd Hoffmann bool set_vol = false; 771f1ae32a1SGerd Hoffmann 772f1ae32a1SGerd Hoffmann switch (aid) { 773f1ae32a1SGerd Hoffmann case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): 7743e44607eSKővágó, Zoltán s->out.vol.mute = data[0] & 1; 775f1ae32a1SGerd Hoffmann set_vol = true; 776f1ae32a1SGerd Hoffmann ret = 0; 777f1ae32a1SGerd Hoffmann break; 778f1ae32a1SGerd Hoffmann case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): 7793e44607eSKővágó, Zoltán if (cn < USBAUDIO_MAX_CHANNELS(s)) { 780f1ae32a1SGerd Hoffmann uint16_t vol = data[0] + (data[1] << 8); 781f1ae32a1SGerd Hoffmann 782f1ae32a1SGerd Hoffmann if (s->debug) { 7833e44607eSKővágó, Zoltán fprintf(stderr, "usb-audio: cn %d vol %04x\n", cn, 7843e44607eSKővágó, Zoltán (uint16_t)vol); 785f1ae32a1SGerd Hoffmann } 786f1ae32a1SGerd Hoffmann 787f1ae32a1SGerd Hoffmann vol -= 0x8000; 788f1ae32a1SGerd Hoffmann vol = (vol * 255 + 0x4400) / 0x8800; 789f1ae32a1SGerd Hoffmann if (vol > 255) { 790f1ae32a1SGerd Hoffmann vol = 255; 791f1ae32a1SGerd Hoffmann } 792f1ae32a1SGerd Hoffmann 7933e44607eSKővágó, Zoltán s->out.vol.vol[cn] = vol; 794f1ae32a1SGerd Hoffmann set_vol = true; 795f1ae32a1SGerd Hoffmann ret = 0; 796f1ae32a1SGerd Hoffmann } 797f1ae32a1SGerd Hoffmann break; 798f1ae32a1SGerd Hoffmann } 799f1ae32a1SGerd Hoffmann 800f1ae32a1SGerd Hoffmann if (set_vol) { 801f1ae32a1SGerd Hoffmann if (s->debug) { 8023e44607eSKővágó, Zoltán int i; 8033e44607eSKővágó, Zoltán fprintf(stderr, "usb-audio: mute %d", s->out.vol.mute); 8043e44607eSKővágó, Zoltán for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { 8053e44607eSKővágó, Zoltán fprintf(stderr, ", vol[%d] %3d", i, s->out.vol.vol[i]); 806f1ae32a1SGerd Hoffmann } 8073e44607eSKővágó, Zoltán fprintf(stderr, "\n"); 8083e44607eSKővágó, Zoltán } 8093e44607eSKővágó, Zoltán audio_set_volume_out(s->out.voice, &s->out.vol); 810f1ae32a1SGerd Hoffmann } 811f1ae32a1SGerd Hoffmann 812f1ae32a1SGerd Hoffmann return ret; 813f1ae32a1SGerd Hoffmann } 814f1ae32a1SGerd Hoffmann 8159a77a0f5SHans de Goede static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, 816f1ae32a1SGerd Hoffmann int request, int value, int index, 817f1ae32a1SGerd Hoffmann int length, uint8_t *data) 818f1ae32a1SGerd Hoffmann { 8190389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 820f1ae32a1SGerd Hoffmann int ret = 0; 821f1ae32a1SGerd Hoffmann 822f1ae32a1SGerd Hoffmann if (s->debug) { 823f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: control transaction: " 824f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 825f1ae32a1SGerd Hoffmann request, value, index, length); 826f1ae32a1SGerd Hoffmann } 827f1ae32a1SGerd Hoffmann 828f1ae32a1SGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 829f1ae32a1SGerd Hoffmann if (ret >= 0) { 8309a77a0f5SHans de Goede return; 831f1ae32a1SGerd Hoffmann } 832f1ae32a1SGerd Hoffmann 833f1ae32a1SGerd Hoffmann switch (request) { 834f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_CUR: 835f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MIN: 836f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_MAX: 837f1ae32a1SGerd Hoffmann case ClassInterfaceRequest | CR_GET_RES: 838f1ae32a1SGerd Hoffmann ret = usb_audio_get_control(s, request & 0xff, value, index, 839f1ae32a1SGerd Hoffmann length, data); 840f1ae32a1SGerd Hoffmann if (ret < 0) { 841f1ae32a1SGerd Hoffmann if (s->debug) { 842f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: get control\n"); 843f1ae32a1SGerd Hoffmann } 844f1ae32a1SGerd Hoffmann goto fail; 845f1ae32a1SGerd Hoffmann } 8469a77a0f5SHans de Goede p->actual_length = ret; 847f1ae32a1SGerd Hoffmann break; 848f1ae32a1SGerd Hoffmann 849f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_CUR: 850f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MIN: 851f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_MAX: 852f1ae32a1SGerd Hoffmann case ClassInterfaceOutRequest | CR_SET_RES: 853f1ae32a1SGerd Hoffmann ret = usb_audio_set_control(s, request & 0xff, value, index, 854f1ae32a1SGerd Hoffmann length, data); 855f1ae32a1SGerd Hoffmann if (ret < 0) { 856f1ae32a1SGerd Hoffmann if (s->debug) { 857f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: fail: set control\n"); 858f1ae32a1SGerd Hoffmann } 859f1ae32a1SGerd Hoffmann goto fail; 860f1ae32a1SGerd Hoffmann } 861f1ae32a1SGerd Hoffmann break; 862f1ae32a1SGerd Hoffmann 863f1ae32a1SGerd Hoffmann default: 864f1ae32a1SGerd Hoffmann fail: 865f1ae32a1SGerd Hoffmann if (s->debug) { 866f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed control transaction: " 867f1ae32a1SGerd Hoffmann "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", 868f1ae32a1SGerd Hoffmann request, value, index, length); 869f1ae32a1SGerd Hoffmann } 8709a77a0f5SHans de Goede p->status = USB_RET_STALL; 871f1ae32a1SGerd Hoffmann break; 872f1ae32a1SGerd Hoffmann } 873f1ae32a1SGerd Hoffmann } 874f1ae32a1SGerd Hoffmann 875f1ae32a1SGerd Hoffmann static void usb_audio_set_interface(USBDevice *dev, int iface, 876f1ae32a1SGerd Hoffmann int old, int value) 877f1ae32a1SGerd Hoffmann { 8780389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 879f1ae32a1SGerd Hoffmann 880f1ae32a1SGerd Hoffmann if (iface == 1) { 881f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, value); 882f1ae32a1SGerd Hoffmann } 883f1ae32a1SGerd Hoffmann } 884f1ae32a1SGerd Hoffmann 885f1ae32a1SGerd Hoffmann static void usb_audio_handle_reset(USBDevice *dev) 886f1ae32a1SGerd Hoffmann { 8870389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 888f1ae32a1SGerd Hoffmann 889f1ae32a1SGerd Hoffmann if (s->debug) { 890f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: reset\n"); 891f1ae32a1SGerd Hoffmann } 892f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 893f1ae32a1SGerd Hoffmann } 894f1ae32a1SGerd Hoffmann 8959a77a0f5SHans de Goede static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) 896f1ae32a1SGerd Hoffmann { 897f1ae32a1SGerd Hoffmann if (s->out.altset == ALTSET_OFF) { 8989a77a0f5SHans de Goede p->status = USB_RET_STALL; 8999a77a0f5SHans de Goede return; 900f1ae32a1SGerd Hoffmann } 901f1ae32a1SGerd Hoffmann 9023e44607eSKővágó, Zoltán streambuf_put(&s->out.buf, p, s->out.channels); 9039a77a0f5SHans de Goede if (p->actual_length < p->iov.size && s->debug > 1) { 904f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", 9059a77a0f5SHans de Goede p->iov.size - p->actual_length); 9069a77a0f5SHans de Goede } 907f1ae32a1SGerd Hoffmann } 908f1ae32a1SGerd Hoffmann 9099a77a0f5SHans de Goede static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) 910f1ae32a1SGerd Hoffmann { 911f1ae32a1SGerd Hoffmann USBAudioState *s = (USBAudioState *) dev; 912f1ae32a1SGerd Hoffmann 9139a77a0f5SHans de Goede if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { 9149a77a0f5SHans de Goede usb_audio_handle_dataout(s, p); 9159a77a0f5SHans de Goede return; 916f1ae32a1SGerd Hoffmann } 917f1ae32a1SGerd Hoffmann 9189a77a0f5SHans de Goede p->status = USB_RET_STALL; 9199a77a0f5SHans de Goede if (s->debug) { 920f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: failed data transaction: " 921f1ae32a1SGerd Hoffmann "pid 0x%x ep 0x%x len 0x%zx\n", 922f1ae32a1SGerd Hoffmann p->pid, p->ep->nr, p->iov.size); 923f1ae32a1SGerd Hoffmann } 924f1ae32a1SGerd Hoffmann } 925f1ae32a1SGerd Hoffmann 926*b69c3c21SMarkus Armbruster static void usb_audio_unrealize(USBDevice *dev) 927f1ae32a1SGerd Hoffmann { 9280389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 929f1ae32a1SGerd Hoffmann 930f1ae32a1SGerd Hoffmann if (s->debug) { 931f1ae32a1SGerd Hoffmann fprintf(stderr, "usb-audio: destroy\n"); 932f1ae32a1SGerd Hoffmann } 933f1ae32a1SGerd Hoffmann 934f1ae32a1SGerd Hoffmann usb_audio_set_output_altset(s, ALTSET_OFF); 935f1ae32a1SGerd Hoffmann AUD_close_out(&s->card, s->out.voice); 936f1ae32a1SGerd Hoffmann AUD_remove_card(&s->card); 937f1ae32a1SGerd Hoffmann 938f1ae32a1SGerd Hoffmann streambuf_fini(&s->out.buf); 939f1ae32a1SGerd Hoffmann } 940f1ae32a1SGerd Hoffmann 9415450eeaaSGonglei static void usb_audio_realize(USBDevice *dev, Error **errp) 942f1ae32a1SGerd Hoffmann { 9430389a0b1SGonglei USBAudioState *s = USB_AUDIO(dev); 9443e44607eSKővágó, Zoltán int i; 9453e44607eSKővágó, Zoltán 9463e44607eSKővágó, Zoltán dev->usb_desc = s->multi ? &desc_audio_multi : &desc_audio; 947f1ae32a1SGerd Hoffmann 9489d55d1adSGerd Hoffmann usb_desc_create_serial(dev); 949f1ae32a1SGerd Hoffmann usb_desc_init(dev); 950f1ae32a1SGerd Hoffmann s->dev.opaque = s; 9510389a0b1SGonglei AUD_register_card(TYPE_USB_AUDIO, &s->card); 952f1ae32a1SGerd Hoffmann 953f1ae32a1SGerd Hoffmann s->out.altset = ALTSET_OFF; 9543e44607eSKővágó, Zoltán s->out.vol.mute = false; 9553e44607eSKővágó, Zoltán for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) { 9563e44607eSKővágó, Zoltán s->out.vol.vol[i] = 240; /* 0 dB */ 9573e44607eSKővágó, Zoltán } 9583e44607eSKővágó, Zoltán 9593e44607eSKővágó, Zoltán usb_audio_reinit(dev, 2); 9603e44607eSKővágó, Zoltán } 9613e44607eSKővágó, Zoltán 9623e44607eSKővágó, Zoltán static void usb_audio_reinit(USBDevice *dev, unsigned channels) 9633e44607eSKővágó, Zoltán { 9643e44607eSKővágó, Zoltán USBAudioState *s = USB_AUDIO(dev); 9653e44607eSKővágó, Zoltán 9663e44607eSKővágó, Zoltán s->out.channels = channels; 9673e44607eSKővágó, Zoltán if (!s->buffer_user) { 9683e44607eSKővágó, Zoltán s->buffer = 32 * USBAUDIO_PACKET_SIZE(s->out.channels); 9693e44607eSKővágó, Zoltán } else { 9703e44607eSKővágó, Zoltán s->buffer = s->buffer_user; 9713e44607eSKővágó, Zoltán } 9723e44607eSKővágó, Zoltán 9733e44607eSKővágó, Zoltán s->out.vol.channels = s->out.channels; 974f1ae32a1SGerd Hoffmann s->out.as.freq = USBAUDIO_SAMPLE_RATE; 9753e44607eSKővágó, Zoltán s->out.as.nchannels = s->out.channels; 97685bc5852SKővágó, Zoltán s->out.as.fmt = AUDIO_FORMAT_S16; 977f1ae32a1SGerd Hoffmann s->out.as.endianness = 0; 9783e44607eSKővágó, Zoltán streambuf_init(&s->out.buf, s->buffer, s->out.channels); 979f1ae32a1SGerd Hoffmann 9800389a0b1SGonglei s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO, 981f1ae32a1SGerd Hoffmann s, output_callback, &s->out.as); 9823e44607eSKővágó, Zoltán audio_set_volume_out(s->out.voice, &s->out.vol); 983f1ae32a1SGerd Hoffmann AUD_set_active_out(s->out.voice, 0); 984f1ae32a1SGerd Hoffmann } 985f1ae32a1SGerd Hoffmann 986f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_audio = { 9870389a0b1SGonglei .name = TYPE_USB_AUDIO, 988f1ae32a1SGerd Hoffmann .unmigratable = 1, 989f1ae32a1SGerd Hoffmann }; 990f1ae32a1SGerd Hoffmann 991f1ae32a1SGerd Hoffmann static Property usb_audio_properties[] = { 99288e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(USBAudioState, card), 993f1ae32a1SGerd Hoffmann DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), 9943e44607eSKővágó, Zoltán DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), 9953e44607eSKővágó, Zoltán DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), 996f1ae32a1SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 997f1ae32a1SGerd Hoffmann }; 998f1ae32a1SGerd Hoffmann 999f1ae32a1SGerd Hoffmann static void usb_audio_class_init(ObjectClass *klass, void *data) 1000f1ae32a1SGerd Hoffmann { 1001f1ae32a1SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 1002f1ae32a1SGerd Hoffmann USBDeviceClass *k = USB_DEVICE_CLASS(klass); 1003f1ae32a1SGerd Hoffmann 1004f1ae32a1SGerd Hoffmann dc->vmsd = &vmstate_usb_audio; 10054f67d30bSMarc-André Lureau device_class_set_props(dc, usb_audio_properties); 1006125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 1007f1ae32a1SGerd Hoffmann k->product_desc = "QEMU USB Audio Interface"; 10085450eeaaSGonglei k->realize = usb_audio_realize; 1009f1ae32a1SGerd Hoffmann k->handle_reset = usb_audio_handle_reset; 1010f1ae32a1SGerd Hoffmann k->handle_control = usb_audio_handle_control; 1011f1ae32a1SGerd Hoffmann k->handle_data = usb_audio_handle_data; 1012c4fe9700SMarc-André Lureau k->unrealize = usb_audio_unrealize; 1013f1ae32a1SGerd Hoffmann k->set_interface = usb_audio_set_interface; 1014f1ae32a1SGerd Hoffmann } 1015f1ae32a1SGerd Hoffmann 10168c43a6f0SAndreas Färber static const TypeInfo usb_audio_info = { 10170389a0b1SGonglei .name = TYPE_USB_AUDIO, 1018f1ae32a1SGerd Hoffmann .parent = TYPE_USB_DEVICE, 1019f1ae32a1SGerd Hoffmann .instance_size = sizeof(USBAudioState), 1020f1ae32a1SGerd Hoffmann .class_init = usb_audio_class_init, 1021f1ae32a1SGerd Hoffmann }; 1022f1ae32a1SGerd Hoffmann 1023f1ae32a1SGerd Hoffmann static void usb_audio_register_types(void) 1024f1ae32a1SGerd Hoffmann { 1025f1ae32a1SGerd Hoffmann type_register_static(&usb_audio_info); 10260389a0b1SGonglei usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL); 1027f1ae32a1SGerd Hoffmann } 1028f1ae32a1SGerd Hoffmann 1029f1ae32a1SGerd Hoffmann type_init(usb_audio_register_types) 1030