All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gerd Hoffmann <kraxel@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kővágó@redhat.com, Zoltán <DirtY.iCE.hu@gmail.com>,
	"Gerd Hoffmann" <kraxel@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>
Subject: [Qemu-devel] [PULL 23/26] usb-audio: support more than two channels of audio
Date: Thu, 19 Sep 2019 10:36:26 +0200	[thread overview]
Message-ID: <20190919083629.29998-24-kraxel@redhat.com> (raw)
In-Reply-To: <20190919083629.29998-1-kraxel@redhat.com>

From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>

This commit adds support for 5.1 and 7.1 audio playback.  This commit
adds a new property to usb-audio:

* multi=on|off
  Whether to enable the 5.1 and 7.1 audio support.  When off (default)
  it continues to emulate the old stereo-only device.  When on, it
  emulates a slightly different audio device that supports 5.1 and 7.1
  audio.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Message-id: 17a517c599bf8e906f41430b97bbe07ebe7eeef5.1568574965.git.DirtY.iCE.hu@gmail.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/dev-audio.c | 419 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 366 insertions(+), 53 deletions(-)

diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 74c99b1f1204..e42bdfbdc101 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -37,11 +37,15 @@
 #include "desc.h"
 #include "audio/audio.h"
 
+static void usb_audio_reinit(USBDevice *dev, unsigned channels);
+
 #define USBAUDIO_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
 #define USBAUDIO_PRODUCT_NUM    0x0002
 
 #define DEV_CONFIG_VALUE        1 /* The one and only */
 
+#define USBAUDIO_MAX_CHANNELS(s) (s->multi ? 8 : 2)
+
 /* Descriptor subtypes for AC interfaces */
 #define DST_AC_HEADER           1
 #define DST_AC_INPUT_TERMINAL   2
@@ -80,6 +84,27 @@ static const USBDescStrings usb_audio_stringtable = {
     [STRING_REAL_STREAM]        = "Audio Output - 48 kHz Stereo",
 };
 
+/*
+ * A USB audio device supports an arbitrary number of alternate
+ * interface settings for each interface.  Each corresponds to a block
+ * diagram of parameterized blocks.  This can thus refer to things like
+ * number of channels, data rates, or in fact completely different
+ * block diagrams.  Alternative setting 0 is always the null block diagram,
+ * which is used by a disabled device.
+ */
+enum usb_audio_altset {
+    ALTSET_OFF    = 0x00,         /* No endpoint */
+    ALTSET_STEREO = 0x01,         /* Single endpoint */
+    ALTSET_51     = 0x02,
+    ALTSET_71     = 0x03,
+};
+
+static unsigned altset_channels[] = {
+    [ALTSET_STEREO] = 2,
+    [ALTSET_51]     = 6,
+    [ALTSET_71]     = 8,
+};
+
 #define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
 #define U24(x) U16(x), (((x) >> 16) & 0xff)
 #define U32(x) U24(x), (((x) >> 24) & 0xff)
@@ -87,7 +112,8 @@ static const USBDescStrings usb_audio_stringtable = {
 /*
  * A Basic Audio Device uses these specific values
  */
-#define USBAUDIO_PACKET_SIZE     192
+#define USBAUDIO_PACKET_SIZE_BASE 96
+#define USBAUDIO_PACKET_SIZE(channels) (USBAUDIO_PACKET_SIZE_BASE * channels)
 #define USBAUDIO_SAMPLE_RATE     48000
 #define USBAUDIO_PACKET_INTERVAL 1
 
@@ -121,7 +147,7 @@ static const USBDescIface desc_iface[] = {
                     0x01,                       /*  u8  bTerminalID */
                     U16(0x0101),                /* u16  wTerminalType */
                     0x00,                       /*  u8  bAssocTerminal */
-                    0x02,                       /* u16  bNrChannels */
+                    0x02,                       /*  u8  bNrChannels */
                     U16(0x0003),                /* u16  wChannelConfig */
                     0x00,                       /*  u8  iChannelNames */
                     STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
@@ -156,14 +182,14 @@ static const USBDescIface desc_iface[] = {
         },
     },{
         .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 0,
+        .bAlternateSetting             = ALTSET_OFF,
         .bNumEndpoints                 = 0,
         .bInterfaceClass               = USB_CLASS_AUDIO,
         .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
         .iInterface                    = STRING_NULL_STREAM,
     },{
         .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 1,
+        .bAlternateSetting             = ALTSET_STEREO,
         .bNumEndpoints                 = 1,
         .bInterfaceClass               = USB_CLASS_AUDIO,
         .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
@@ -199,7 +225,7 @@ static const USBDescIface desc_iface[] = {
             {
                 .bEndpointAddress      = USB_DIR_OUT | 0x01,
                 .bmAttributes          = 0x0d,
-                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE(2),
                 .bInterval             = 1,
                 .is_audio              = 1,
                 /* Stereo Headphone Class-specific
@@ -247,17 +273,274 @@ static const USBDesc desc_audio = {
     .str  = usb_audio_stringtable,
 };
 
-/*
- * A USB audio device supports an arbitrary number of alternate
- * interface settings for each interface.  Each corresponds to a block
- * diagram of parameterized blocks.  This can thus refer to things like
- * number of channels, data rates, or in fact completely different
- * block diagrams.  Alternative setting 0 is always the null block diagram,
- * which is used by a disabled device.
- */
-enum usb_audio_altset {
-    ALTSET_OFF  = 0x00,         /* No endpoint */
-    ALTSET_ON   = 0x01,         /* Single endpoint */
+/* multi channel compatible desc */
+
+static const USBDescIface desc_iface_multi[] = {
+    {
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
+        .bInterfaceProtocol            = 0x04,
+        .iInterface                    = STRING_USBAUDIO_CONTROL,
+        .ndesc                         = 4,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-Specific AC Interface Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
+                    U16(0x0100),                /* u16  bcdADC */
+                    U16(0x38),                  /* u16  wTotalLength */
+                    0x01,                       /*  u8  bInCollection */
+                    0x01,                       /*  u8  baInterfaceNr */
+                }
+            },{
+                /* Generic Stereo Input Terminal ID1 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0c,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalID */
+                    U16(0x0101),                /* u16  wTerminalType */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x08,                       /*  u8  bNrChannels */
+                    U16(0x063f),                /* u16  wChannelConfig */
+                    0x00,                       /*  u8  iChannelNames */
+                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
+                }
+            },{
+                /* Generic Stereo Feature Unit ID2 Descriptor */
+                .data = (uint8_t[]) {
+                    0x19,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
+                    0x02,                       /*  u8  bUnitID */
+                    0x01,                       /*  u8  bSourceID */
+                    0x02,                       /*  u8  bControlSize */
+                    U16(0x0001),                /* u16  bmaControls(0) */
+                    U16(0x0002),                /* u16  bmaControls(1) */
+                    U16(0x0002),                /* u16  bmaControls(2) */
+                    U16(0x0002),                /* u16  bmaControls(3) */
+                    U16(0x0002),                /* u16  bmaControls(4) */
+                    U16(0x0002),                /* u16  bmaControls(5) */
+                    U16(0x0002),                /* u16  bmaControls(6) */
+                    U16(0x0002),                /* u16  bmaControls(7) */
+                    U16(0x0002),                /* u16  bmaControls(8) */
+                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
+                }
+            },{
+                /* Headphone Ouptut Terminal ID3 Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
+                    0x03,                       /*  u8  bUnitID */
+                    U16(0x0301),                /* u16  wTerminalType (SPK) */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /*  u8  bSourceID */
+                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
+                }
+            }
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = ALTSET_OFF,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_NULL_STREAM,
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = ALTSET_STEREO,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x02,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE(2),
+                .bInterval             = 1,
+                .is_audio              = 1,
+                /* Stereo Headphone Class-specific
+                   AS Audio Data Endpoint Descriptor */
+                .extra = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x00,                       /*  u8  bmAttributes */
+                    0x00,                       /*  u8  bLockDelayUnits */
+                    U16(0x0000),                /* u16  wLockDelay */
+                },
+            },
+        }
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = ALTSET_51,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x06,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE(6),
+                .bInterval             = 1,
+                .is_audio              = 1,
+                /* Stereo Headphone Class-specific
+                   AS Audio Data Endpoint Descriptor */
+                .extra = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x00,                       /*  u8  bmAttributes */
+                    0x00,                       /*  u8  bLockDelayUnits */
+                    U16(0x0000),                /* u16  wLockDelay */
+                },
+            },
+        }
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = ALTSET_71,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x08,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE(8),
+                .bInterval             = 1,
+                .is_audio              = 1,
+                /* Stereo Headphone Class-specific
+                   AS Audio Data Endpoint Descriptor */
+                .extra = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x00,                       /*  u8  bmAttributes */
+                    0x00,                       /*  u8  bLockDelayUnits */
+                    U16(0x0000),                /* u16  wLockDelay */
+                },
+            },
+        }
+    }
+};
+
+static const USBDescDevice desc_device_multi = {
+    .bcdUSB                        = 0x0100,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_CONFIG_VALUE,
+            .iConfiguration        = STRING_CONFIG,
+            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface_multi),
+            .ifs = desc_iface_multi,
+        }
+    },
+};
+
+static const USBDesc desc_audio_multi = {
+    .id = {
+        .idVendor          = USBAUDIO_VENDOR_NUM,
+        .idProduct         = USBAUDIO_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device_multi,
+    .str  = usb_audio_stringtable,
 };
 
 /*
@@ -300,10 +583,11 @@ struct streambuf {
     uint32_t cons;
 };
 
-static void streambuf_init(struct streambuf *buf, uint32_t size)
+static void streambuf_init(struct streambuf *buf, uint32_t size,
+                           uint32_t channels)
 {
     g_free(buf->data);
-    buf->size = size - (size % USBAUDIO_PACKET_SIZE);
+    buf->size = size - (size % USBAUDIO_PACKET_SIZE(channels));
     buf->data = g_malloc(buf->size);
     buf->prod = 0;
     buf->cons = 0;
@@ -315,21 +599,21 @@ static void streambuf_fini(struct streambuf *buf)
     buf->data = NULL;
 }
 
-static int streambuf_put(struct streambuf *buf, USBPacket *p)
+static int streambuf_put(struct streambuf *buf, USBPacket *p, uint32_t channels)
 {
     uint32_t free = buf->size - (buf->prod - buf->cons);
 
-    if (free < USBAUDIO_PACKET_SIZE) {
+    if (free < USBAUDIO_PACKET_SIZE(channels)) {
         return 0;
     }
-    if (p->iov.size != USBAUDIO_PACKET_SIZE) {
+    if (p->iov.size != USBAUDIO_PACKET_SIZE(channels)) {
         return 0;
     }
 
     usb_packet_copy(p, buf->data + (buf->prod % buf->size),
-                    USBAUDIO_PACKET_SIZE);
-    buf->prod += USBAUDIO_PACKET_SIZE;
-    return USBAUDIO_PACKET_SIZE;
+                    USBAUDIO_PACKET_SIZE(channels));
+    buf->prod += USBAUDIO_PACKET_SIZE(channels);
+    return USBAUDIO_PACKET_SIZE(channels);
 }
 
 static uint8_t *streambuf_get(struct streambuf *buf, size_t *len)
@@ -357,14 +641,15 @@ typedef struct USBAudioState {
         enum usb_audio_altset altset;
         struct audsettings as;
         SWVoiceOut *voice;
-        bool mute;
-        uint8_t vol[2];
+        Volume vol;
         struct streambuf buf;
+        uint32_t channels;
     } out;
 
     /* properties */
     uint32_t debug;
-    uint32_t buffer;
+    uint32_t buffer_user, buffer;
+    bool multi;
 } USBAudioState;
 
 #define TYPE_USB_AUDIO "usb-audio"
@@ -397,10 +682,15 @@ static int usb_audio_set_output_altset(USBAudioState *s, int altset)
 {
     switch (altset) {
     case ALTSET_OFF:
-        streambuf_init(&s->out.buf, s->buffer);
         AUD_set_active_out(s->out.voice, false);
         break;
-    case ALTSET_ON:
+    case ALTSET_STEREO:
+    case ALTSET_51:
+    case ALTSET_71:
+        if (s->out.channels != altset_channels[altset]) {
+            usb_audio_reinit(USB_DEVICE(s), altset_channels[altset]);
+        }
+        streambuf_init(&s->out.buf, s->buffer, s->out.channels);
         AUD_set_active_out(s->out.voice, true);
         break;
     default:
@@ -431,33 +721,33 @@ static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
 
     switch (aid) {
     case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
-        data[0] = s->out.mute;
+        data[0] = s->out.vol.mute;
         ret = 1;
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
-        if (cn < 2) {
-            uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
+        if (cn < USBAUDIO_MAX_CHANNELS(s)) {
+            uint16_t vol = (s->out.vol.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
             data[0] = vol;
             data[1] = vol >> 8;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
-        if (cn < 2) {
+        if (cn < USBAUDIO_MAX_CHANNELS(s)) {
             data[0] = 0x01;
             data[1] = 0x80;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
-        if (cn < 2) {
+        if (cn < USBAUDIO_MAX_CHANNELS(s)) {
             data[0] = 0x00;
             data[1] = 0x08;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
-        if (cn < 2) {
+        if (cn < USBAUDIO_MAX_CHANNELS(s)) {
             data[0] = 0x88;
             data[1] = 0x00;
             ret = 2;
@@ -479,16 +769,17 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
 
     switch (aid) {
     case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
-        s->out.mute = data[0] & 1;
+        s->out.vol.mute = data[0] & 1;
         set_vol = true;
         ret = 0;
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
-        if (cn < 2) {
+        if (cn < USBAUDIO_MAX_CHANNELS(s)) {
             uint16_t vol = data[0] + (data[1] << 8);
 
             if (s->debug) {
-                fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
+                fprintf(stderr, "usb-audio: cn %d vol %04x\n", cn,
+                        (uint16_t)vol);
             }
 
             vol -= 0x8000;
@@ -497,7 +788,7 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
                 vol = 255;
             }
 
-            s->out.vol[cn] = vol;
+            s->out.vol.vol[cn] = vol;
             set_vol = true;
             ret = 0;
         }
@@ -506,11 +797,14 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
 
     if (set_vol) {
         if (s->debug) {
-            fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
-                    s->out.mute, s->out.vol[0], s->out.vol[1]);
+            int i;
+            fprintf(stderr, "usb-audio: mute %d", s->out.vol.mute);
+            for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) {
+                fprintf(stderr, ", vol[%d] %3d", i, s->out.vol.vol[i]);
+            }
+            fprintf(stderr, "\n");
         }
-        AUD_set_volume_out(s->out.voice, s->out.mute,
-                           s->out.vol[0], s->out.vol[1]);
+        audio_set_volume_out(s->out.voice, &s->out.vol);
     }
 
     return ret;
@@ -603,7 +897,7 @@ static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
         return;
     }
 
-    streambuf_put(&s->out.buf, p);
+    streambuf_put(&s->out.buf, p, s->out.channels);
     if (p->actual_length < p->iov.size && s->debug > 1) {
         fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
                 p->iov.size - p->actual_length);
@@ -645,6 +939,9 @@ static void usb_audio_unrealize(USBDevice *dev, Error **errp)
 static void usb_audio_realize(USBDevice *dev, Error **errp)
 {
     USBAudioState *s = USB_AUDIO(dev);
+    int i;
+
+    dev->usb_desc = s->multi ? &desc_audio_multi : &desc_audio;
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
@@ -652,18 +949,35 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
     AUD_register_card(TYPE_USB_AUDIO, &s->card);
 
     s->out.altset        = ALTSET_OFF;
-    s->out.mute          = false;
-    s->out.vol[0]        = 240; /* 0 dB */
-    s->out.vol[1]        = 240; /* 0 dB */
+    s->out.vol.mute      = false;
+    for (i = 0; i < USBAUDIO_MAX_CHANNELS(s); ++i) {
+        s->out.vol.vol[i] = 240; /* 0 dB */
+    }
+
+    usb_audio_reinit(dev, 2);
+}
+
+static void usb_audio_reinit(USBDevice *dev, unsigned channels)
+{
+    USBAudioState *s = USB_AUDIO(dev);
+
+    s->out.channels      = channels;
+    if (!s->buffer_user) {
+        s->buffer = 32 * USBAUDIO_PACKET_SIZE(s->out.channels);
+    } else {
+        s->buffer = s->buffer_user;
+    }
+
+    s->out.vol.channels  = s->out.channels;
     s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
-    s->out.as.nchannels  = 2;
+    s->out.as.nchannels  = s->out.channels;
     s->out.as.fmt        = AUDIO_FORMAT_S16;
     s->out.as.endianness = 0;
-    streambuf_init(&s->out.buf, s->buffer);
+    streambuf_init(&s->out.buf, s->buffer, s->out.channels);
 
     s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO,
                                 s, output_callback, &s->out.as);
-    AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+    audio_set_volume_out(s->out.voice, &s->out.vol);
     AUD_set_active_out(s->out.voice, 0);
 }
 
@@ -675,8 +989,8 @@ static const VMStateDescription vmstate_usb_audio = {
 static Property usb_audio_properties[] = {
     DEFINE_AUDIO_PROPERTIES(USBAudioState, card),
     DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
-    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
-                       32 * USBAUDIO_PACKET_SIZE),
+    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0),
+    DEFINE_PROP_BOOL("multi", USBAudioState, multi, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -689,7 +1003,6 @@ static void usb_audio_class_init(ObjectClass *klass, void *data)
     dc->props         = usb_audio_properties;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     k->product_desc   = "QEMU USB Audio Interface";
-    k->usb_desc       = &desc_audio;
     k->realize        = usb_audio_realize;
     k->handle_reset   = usb_audio_handle_reset;
     k->handle_control = usb_audio_handle_control;
-- 
2.18.1



  parent reply	other threads:[~2019-09-19  8:59 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-19  8:36 [Qemu-devel] [PULL 00/26] Audio 20190919 patches Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 01/26] audio: api for mixeng code free backends Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 02/26] alsaaudio: port to the new audio backend api Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 03/26] coreaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 04/26] dsoundaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 05/26] noaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 06/26] ossaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 07/26] paaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 08/26] sdlaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 09/26] spiceaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 10/26] wavaudio: " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 11/26] audio: remove remains of the old " Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 12/26] audio: unify input and output mixeng buffer management Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 13/26] audio: common rate control code for timer based outputs Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 14/26] audio: split ctl_* functions into enable_* and volume_* Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 15/26] audio: add mixeng option (documentation) Gerd Hoffmann
2019-09-19 15:27   ` Eric Blake
2019-09-19  8:36 ` [Qemu-devel] [PULL 16/26] audio: make mixeng optional Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 17/26] paaudio: get/put_buffer functions Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 18/26] audio: support more than two channels in volume setting Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 19/26] audio: replace shift in audio_pcm_info with bytes_per_frame Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 20/26] audio: basic support for multichannel audio Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 21/26] paaudio: channel-map option Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 22/26] usb-audio: do not count on avail bytes actually available Gerd Hoffmann
2019-09-19  8:36 ` Gerd Hoffmann [this message]
2019-09-19  8:36 ` [Qemu-devel] [PULL 24/26] usbaudio: change playback counters to 64 bit Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 25/26] audio: fix buffer-length typo in documentation Gerd Hoffmann
2019-09-19  8:36 ` [Qemu-devel] [PULL 26/26] audio: fix ALSA period-length " Gerd Hoffmann
2019-09-19 15:15 ` [Qemu-devel] [PULL 00/26] Audio 20190919 patches Peter Maydell
2019-09-19 15:28   ` Eric Blake
2019-09-19 21:26     ` Zoltán Kővágó

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190919083629.29998-24-kraxel@redhat.com \
    --to=kraxel@redhat.com \
    --cc=DirtY.iCE.hu@gmail.com \
    --cc=Kővágó@redhat.com \
    --cc=armbru@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.