get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2196803/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2196803,
    "url": "http://patchwork.ozlabs.org/api/patches/2196803/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260216-audio-v1-20-e676662e4514@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260216-audio-v1-20-e676662e4514@redhat.com>",
    "list_archive_url": null,
    "date": "2026-02-16T11:15:09",
    "name": "[20/85] audio: introduce AudioMixengBackend",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "08275b993c3614ed6b0d87ab3959361f50f9385e",
    "submitter": {
        "id": 66774,
        "url": "http://patchwork.ozlabs.org/api/people/66774/?format=api",
        "name": "Marc-André Lureau",
        "email": "marcandre.lureau@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260216-audio-v1-20-e676662e4514@redhat.com/mbox/",
    "series": [
        {
            "id": 492294,
            "url": "http://patchwork.ozlabs.org/api/series/492294/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=492294",
            "date": "2026-02-16T11:14:52",
            "name": "audio: cleanups & add a manual test",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/492294/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2196803/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2196803/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=gsEFvRwx;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fF0g337c1z1xtN\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 16 Feb 2026 22:19:59 +1100 (AEDT)",
            "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1vrwdU-0008RN-GQ; Mon, 16 Feb 2026 06:19:29 -0500",
            "from eggs.gnu.org ([2001:470:142:3::10])\n by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1vrwaw-0005Ml-5A\n for qemu-devel@nongnu.org; Mon, 16 Feb 2026 06:17:05 -0500",
            "from us-smtp-delivery-124.mimecast.com ([170.10.133.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1vrwar-0007T5-Gg\n for qemu-devel@nongnu.org; Mon, 16 Feb 2026 06:16:48 -0500",
            "from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-424-CplpqzP1OvmAB3Kn44eTAg-1; Mon,\n 16 Feb 2026 06:16:36 -0500",
            "from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 6A6151956054; Mon, 16 Feb 2026 11:16:34 +0000 (UTC)",
            "from localhost (unknown [10.45.242.26])\n by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 4B6F919560A2; Mon, 16 Feb 2026 11:16:33 +0000 (UTC)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1771240599;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=sXW30UumLYxWWPpS7nnFOzuNZMWZaTtGVKTHNUU8ICg=;\n b=gsEFvRwx80UMMt+xmdaJmTCW3kfiLDUtBdZFSP5x7/2UwCOqLAcfUqiQ4f0YtbWGLp6Kfe\n 8V+ZVCVmVTmhMXkbgUZn/r5nxuPV+jCRrGZGMssMy5q7d4SqqRMxxgLslt588rxfKeCaMS\n wCV0/5oTGbod+fziOM5gYNfAwx/dz1k=",
        "X-MC-Unique": "CplpqzP1OvmAB3Kn44eTAg-1",
        "X-Mimecast-MFC-AGG-ID": "CplpqzP1OvmAB3Kn44eTAg_1771240594",
        "From": "=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>",
        "Date": "Mon, 16 Feb 2026 12:15:09 +0100",
        "Subject": "[PATCH 20/85] audio: introduce AudioMixengBackend",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "8bit",
        "Message-Id": "<20260216-audio-v1-20-e676662e4514@redhat.com>",
        "References": "<20260216-audio-v1-0-e676662e4514@redhat.com>",
        "In-Reply-To": "<20260216-audio-v1-0-e676662e4514@redhat.com>",
        "To": "qemu-devel@nongnu.org",
        "Cc": "Gerd Hoffmann <kraxel@redhat.com>,  Eduardo Habkost <eduardo@habkost.net>,\n Paolo Bonzini <pbonzini@redhat.com>,\n =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= <berrange@redhat.com>, =?utf-8?q?Phil?=\n\t=?utf-8?q?ippe_Mathieu-Daud=C3=A9?= <philmd@linaro.org>,\n  John Snow <jsnow@redhat.com>, Cleber Rosa <crosa@redhat.com>,\n  Christian Schoenebeck <qemu_oss@crudebyte.com>,\n  Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>,\n  Thomas Huth <huth@tuxfamily.org>, Alexandre Ratchov <alex@caoua.org>,\n\t=?utf-8?q?Alex_Benn=C3=A9e?= <alex.bennee@linaro.org>,\n  Laurent Vivier <laurent@vivier.eu>, \"Michael S. Tsirkin\" <mst@redhat.com>,\n  Manos Pitsidianakis <manos.pitsidianakis@linaro.org>,\n  Alistair Francis <alistair@alistair23.me>,\n  \"Edgar E. Iglesias\" <edgar.iglesias@gmail.com>,\n  Peter Maydell <peter.maydell@linaro.org>, qemu-arm@nongnu.org, =?utf-8?q?M?=\n\t=?utf-8?q?arc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>,\n  Mark Cave-Ayland <mark.caveayland@nutanix.com>",
        "X-Developer-Signature": "v=1; a=openpgp-sha256; l=22549;\n i=marcandre.lureau@redhat.com; h=from:subject:message-id;\n bh=TWADWJLqjUZp+XDQueVSRfn8tBkFT5C/9utkI3kOOM4=;\n b=owEBbQKS/ZANAwAKAdro4Ql1lpzlAcsmYgBpkvxPkssa1mN1u6ESQ0aZGPxTzWuVVy2DiToZG\n MV+bwuAtXiJAjMEAAEKAB0WIQSHqb2TP4fGBtJ29i3a6OEJdZac5QUCaZL8TwAKCRDa6OEJdZac\n 5Y1uEACd4Q1CtBw+yEXSIAeDf/q1r3YqrKoOD2ikvWptOvCbNCBv4DlCg+d3Wq2vxrOjkqCVNKO\n 1QBSVapGbVGleviOtkbXtcHyVN6uhf0yalFuloZogV6rWprKrT44ZC8JIyZAoW7ZLH2wPUvrQEh\n wj2qX+MW9XjUdmAlJ5GLL0enZnb4IdelSmn1bafe8AZssgRR+sar0/akXoknUKCNa06f7UWWSrD\n Lax4BrhlZs+Wi1aD/QbpanbXhMuuH7BnPlSx4hg8UVOGLQATDKRXmW+MbVVSAOiJB0uWZ26NxHo\n vv5J8VjGjOxVmhQJV81EhVBo6GFQiAk9A/vByGRcGhmdnJ7tlR3gbzgZux5LZlcug4/0SW/kXtg\n fRWv88N3Fg/gS83stXcBGoogUvbMAUB4YzPppsbHU3zBH0HN/K9Na/u8RJJW/vZWYystk0XZ1a0\n 1AFUlorYx4JFIQW1wVgdNLv8S+9ES0LK5JG0UiwHDke17ZHR20T1IYOuW3r07Fcbh5YTpEpjTs9\n g8IWYTscuFhhMOe9gtCBpaOTuBXy6acOSdygzqWFfO085agZ/BapZqLuBPFIJ13DbkO5jiviLWf\n v01ARZEoJZSnmtAzPYoDEougyLr0FE1m38Xe/dpqyHuwPCiGrimvSWw4UkyrYs4esPiP+jWmKjr\n BJ5GYAEd4zm95cw==",
        "X-Developer-Key": "i=marcandre.lureau@redhat.com; a=openpgp;\n fpr=87A9BD933F87C606D276F62DDAE8E10975969CE5",
        "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.12",
        "Received-SPF": "pass client-ip=170.10.133.124;\n envelope-from=marcandre.lureau@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com",
        "X-Spam_score_int": "-20",
        "X-Spam_score": "-2.1",
        "X-Spam_bar": "--",
        "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001,\n RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001,\n SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "qemu development <qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"
    },
    "content": "Introduce a sub-class for current \"audio_driver\" based implementations.\nFuture AudioBackend implementations can do without it.\n\nNext cleanup will actually remove \"audio_driver\" struct altogether and\nmake the subclass proper QOM objects.\n\nPublic APIs still rely on backend being an AudioMixeng. They will\nassert() if not. This will be addressed later to allow other backends.\n\nNote that the initial naming proposed for this object was AudioDriver,\nhowever the semantics for \"driver\" is already overloaded and leads to\nconfusion, in particular with the QAPI AudiodevDriver. The defining\ncharacteristic is of using QEMU's software mixing engine, so\nAudioMixengBackend.\n\nSigned-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>\nReviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>\n---\n audio/audio_int.h        |  25 ++++++----\n audio/audio_template.h   |  18 +++----\n include/qemu/audio.h     |  17 +++++--\n audio/alsaaudio.c        |   2 +-\n audio/audio.c            | 123 +++++++++++++++++++++++++++++++++++------------\n audio/dbusaudio.c        |   8 +--\n audio/ossaudio.c         |   4 +-\n tests/audio/test-audio.c |   4 +-\n ui/dbus.c                |   6 +--\n 9 files changed, 143 insertions(+), 64 deletions(-)",
    "diff": "diff --git a/audio/audio_int.h b/audio/audio_int.h\nindex a6276375887..06f5160e8df 100644\n--- a/audio/audio_int.h\n+++ b/audio/audio_int.h\n@@ -61,7 +61,7 @@ struct audio_pcm_info {\n     int swap_endianness;\n };\n \n-typedef struct AudioBackend AudioBackend;\n+typedef struct AudioMixengBackend AudioMixengBackend;\n typedef struct SWVoiceCap SWVoiceCap;\n \n typedef struct STSampleBuffer {\n@@ -70,7 +70,7 @@ typedef struct STSampleBuffer {\n } STSampleBuffer;\n \n typedef struct HWVoiceOut {\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n     bool enabled;\n     int poll_mode;\n     bool pending_disable;\n@@ -90,7 +90,7 @@ typedef struct HWVoiceOut {\n } HWVoiceOut;\n \n typedef struct HWVoiceIn {\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n     bool enabled;\n     int poll_mode;\n     struct audio_pcm_info info;\n@@ -110,7 +110,7 @@ typedef struct HWVoiceIn {\n } HWVoiceIn;\n \n struct SWVoiceOut {\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n     struct audio_pcm_info info;\n     t_sample *conv;\n     STSampleBuffer resample_buf;\n@@ -126,7 +126,7 @@ struct SWVoiceOut {\n };\n \n struct SWVoiceIn {\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n     bool active;\n     struct audio_pcm_info info;\n     void *rate;\n@@ -239,8 +239,12 @@ struct SWVoiceCap {\n     QLIST_ENTRY (SWVoiceCap) entries;\n };\n \n-typedef struct AudioBackend {\n-    Object parent;\n+struct AudioMixengBackendClass {\n+    AudioBackendClass parent_class;\n+};\n+\n+struct AudioMixengBackend {\n+    AudioBackend parent_obj;\n \n     struct audio_driver *drv;\n     Audiodev *dev;\n@@ -257,7 +261,7 @@ typedef struct AudioBackend {\n     bool timer_running;\n     uint64_t timer_last;\n     VMChangeStateEntry *vmse;\n-} AudioBackend;\n+};\n \n extern const struct mixeng_volume nominal_volume;\n \n@@ -270,7 +274,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);\n \n int audio_bug (const char *funcname, int cond);\n \n-void audio_run(AudioBackend *s, const char *msg);\n+void audio_run(AudioMixengBackend *s, const char *msg);\n \n const char *audio_application_name(void);\n \n@@ -323,4 +327,7 @@ void audio_create_pdos(Audiodev *dev);\n AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);\n AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);\n \n+#define TYPE_AUDIO_MIXENG_BACKEND \"audio-mixeng-backend\"\n+OBJECT_DECLARE_TYPE(AudioMixengBackend, AudioMixengBackendClass, AUDIO_MIXENG_BACKEND)\n+\n #endif /* QEMU_AUDIO_INT_H */\ndiff --git a/audio/audio_template.h b/audio/audio_template.h\nindex e69e6e74570..a1f78d8748b 100644\n--- a/audio/audio_template.h\n+++ b/audio/audio_template.h\n@@ -36,7 +36,7 @@\n #define HWBUF hw->conv_buf\n #endif\n \n-static void glue(audio_init_nb_voices_, TYPE)(AudioBackend *s,\n+static void glue(audio_init_nb_voices_, TYPE)(AudioMixengBackend *s,\n                                               struct audio_driver *drv, int min_voices)\n {\n     int max_voices = glue (drv->max_voices_, TYPE);\n@@ -221,7 +221,7 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)\n static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)\n {\n     HW *hw = *hwp;\n-    AudioBackend *s = hw->s;\n+    AudioMixengBackend *s = hw->s;\n \n     if (!hw->sw_head.lh_first) {\n #ifdef DAC\n@@ -236,12 +236,12 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)\n     }\n }\n \n-static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioBackend *s, HW *hw)\n+static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioMixengBackend *s, HW *hw)\n {\n     return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;\n }\n \n-static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)\n+static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioMixengBackend *s, HW *hw)\n {\n     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {\n         if (hw->enabled) {\n@@ -251,7 +251,7 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)\n     return NULL;\n }\n \n-static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,\n+static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioMixengBackend *s, HW *hw,\n                                                    struct audsettings *as)\n {\n     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {\n@@ -262,7 +262,7 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,\n     return NULL;\n }\n \n-static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioBackend *s,\n+static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioMixengBackend *s,\n                                              struct audsettings *as)\n {\n     HW *hw;\n@@ -398,7 +398,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)\n     abort();\n }\n \n-static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as)\n+static HW *glue(audio_pcm_hw_add_, TYPE)(AudioMixengBackend *s, struct audsettings *as)\n {\n     HW *hw;\n     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);\n@@ -424,7 +424,7 @@ static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as\n }\n \n static SW *glue(audio_pcm_create_voice_pair_, TYPE)(\n-    AudioBackend *s,\n+    AudioMixengBackend *s,\n     const char *sw_name,\n     const struct audsettings *as\n     )\n@@ -494,7 +494,7 @@ SW *glue (AUD_open_, TYPE) (\n     const struct audsettings *as\n     )\n {\n-    AudioBackend *s = be;\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(be);\n     AudiodevPerDirectionOptions *pdo;\n \n     if (audio_bug(__func__, !be || !name || !callback_fn || !as)) {\ndiff --git a/include/qemu/audio.h b/include/qemu/audio.h\nindex 370e993f080..474d761f0be 100644\n--- a/include/qemu/audio.h\n+++ b/include/qemu/audio.h\n@@ -44,11 +44,21 @@ typedef struct audsettings {\n typedef struct SWVoiceOut SWVoiceOut;\n typedef struct SWVoiceIn SWVoiceIn;\n \n-struct AudioBackendClass {\n+typedef struct AudioBackend {\n+    Object parent_obj;\n+} AudioBackend;\n+\n+typedef struct AudioBackendClass {\n     ObjectClass parent_class;\n-};\n \n-typedef struct AudioBackend AudioBackend;\n+    const char *(*get_id)(AudioBackend *be);\n+#ifdef CONFIG_GIO\n+    bool (*set_dbus_server)(AudioBackend *be,\n+                            GDBusObjectManagerServer *manager,\n+                            bool p2p,\n+                            Error **errp);\n+#endif\n+} AudioBackendClass;\n \n bool AUD_backend_check(AudioBackend **be, Error **errp);\n \n@@ -125,6 +135,7 @@ AudioBackend *audio_be_by_name(const char *name, Error **errp);\n AudioBackend *audio_get_default_audio_be(Error **errp);\n const char *audio_be_get_id(AudioBackend *be);\n #ifdef CONFIG_GIO\n+bool audio_be_can_set_dbus_server(AudioBackend *be);\n bool audio_be_set_dbus_server(AudioBackend *be,\n                               GDBusObjectManagerServer *server,\n                               bool p2p,\ndiff --git a/audio/alsaaudio.c b/audio/alsaaudio.c\nindex 7d7da576dc9..814820e2864 100644\n--- a/audio/alsaaudio.c\n+++ b/audio/alsaaudio.c\n@@ -41,7 +41,7 @@ struct pollhlp {\n     struct pollfd *pfds;\n     int count;\n     int mask;\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n };\n \n typedef struct ALSAVoiceOut {\ndiff --git a/audio/audio.c b/audio/audio.c\nindex 5dccb9b0a36..75a7f694319 100644\n--- a/audio/audio.c\n+++ b/audio/audio.c\n@@ -37,6 +37,7 @@\n #include \"qemu/log.h\"\n #include \"qemu/module.h\"\n #include \"qemu/help_option.h\"\n+#include \"qom/object.h\"\n #include \"system/system.h\"\n #include \"system/replay.h\"\n #include \"system/runstate.h\"\n@@ -384,7 +385,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)\n /*\n  * Capture\n  */\n-static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioBackend *s,\n+static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioMixengBackend *s,\n                                                         struct audsettings *as)\n {\n     CaptureVoiceOut *cap;\n@@ -464,7 +465,7 @@ static void audio_detach_capture (HWVoiceOut *hw)\n \n static int audio_attach_capture (HWVoiceOut *hw)\n {\n-    AudioBackend *s = hw->s;\n+    AudioMixengBackend *s = hw->s;\n     CaptureVoiceOut *cap;\n \n     audio_detach_capture (hw);\n@@ -802,7 +803,7 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)\n /*\n  * Timer\n  */\n-static int audio_is_timer_needed(AudioBackend *s)\n+static int audio_is_timer_needed(AudioMixengBackend *s)\n {\n     HWVoiceIn *hwi = NULL;\n     HWVoiceOut *hwo = NULL;\n@@ -820,7 +821,7 @@ static int audio_is_timer_needed(AudioBackend *s)\n     return 0;\n }\n \n-static void audio_reset_timer(AudioBackend *s)\n+static void audio_reset_timer(AudioMixengBackend *s)\n {\n     if (audio_is_timer_needed(s)) {\n         timer_mod_anticipate_ns(s->ts,\n@@ -842,7 +843,7 @@ static void audio_reset_timer(AudioBackend *s)\n static void audio_timer (void *opaque)\n {\n     int64_t now, diff;\n-    AudioBackend *s = opaque;\n+    AudioMixengBackend *s = opaque;\n \n     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n     diff = now - s->timer_last;\n@@ -925,7 +926,7 @@ void AUD_set_active_out(SWVoiceOut *sw, bool on)\n \n     hw = sw->hw;\n     if (sw->active != on) {\n-        AudioBackend *s = sw->s;\n+        AudioMixengBackend *s = sw->s;\n         SWVoiceOut *temp_sw;\n         SWVoiceCap *sc;\n \n@@ -973,7 +974,7 @@ void AUD_set_active_in(SWVoiceIn *sw, bool on)\n \n     hw = sw->hw;\n     if (sw->active != on) {\n-        AudioBackend *s = sw->s;\n+        AudioMixengBackend *s = sw->s;\n         SWVoiceIn *temp_sw;\n \n         if (on) {\n@@ -1141,7 +1142,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)\n     return clipped;\n }\n \n-static void audio_run_out(AudioBackend *s)\n+static void audio_run_out(AudioMixengBackend *s)\n {\n     HWVoiceOut *hw = NULL;\n     SWVoiceOut *sw;\n@@ -1294,7 +1295,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)\n     return conv;\n }\n \n-static void audio_run_in(AudioBackend *s)\n+static void audio_run_in(AudioMixengBackend *s)\n {\n     HWVoiceIn *hw = NULL;\n \n@@ -1341,7 +1342,7 @@ static void audio_run_in(AudioBackend *s)\n     }\n }\n \n-static void audio_run_capture(AudioBackend *s)\n+static void audio_run_capture(AudioMixengBackend *s)\n {\n     CaptureVoiceOut *cap;\n \n@@ -1388,7 +1389,7 @@ static void audio_run_capture(AudioBackend *s)\n     }\n }\n \n-void audio_run(AudioBackend *s, const char *msg)\n+void audio_run(AudioMixengBackend *s, const char *msg)\n {\n     audio_run_out(s);\n     audio_run_in(s);\n@@ -1561,8 +1562,8 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)\n     return total;\n }\n \n-static bool audio_driver_init(AudioBackend *s, struct audio_driver *drv,\n-                              Audiodev *dev, Error **errp)\n+static bool audio_driver_do_init(AudioMixengBackend *s, struct audio_driver *drv,\n+                                 Audiodev *dev, Error **errp)\n {\n     s->drv_opaque = drv->init(dev, errp);\n     if (!s->drv_opaque) {\n@@ -1594,7 +1595,7 @@ static bool audio_driver_init(AudioBackend *s, struct audio_driver *drv,\n static void audio_vm_change_state_handler (void *opaque, bool running,\n                                            RunState state)\n {\n-    AudioBackend *s = opaque;\n+    AudioMixengBackend *s = opaque;\n     HWVoiceOut *hwo = NULL;\n     HWVoiceIn *hwi = NULL;\n \n@@ -1616,7 +1617,47 @@ static const VMStateDescription vmstate_audio;\n \n static void audio_be_init(Object *obj)\n {\n-    AudioBackend *s = AUDIO_BACKEND(obj);\n+}\n+\n+static void audio_be_finalize(Object *obj)\n+{\n+}\n+\n+static const char *audio_mixeng_backend_get_id(AudioBackend *be)\n+{\n+    return AUDIO_MIXENG_BACKEND(be)->dev->id;\n+}\n+\n+#ifdef CONFIG_GIO\n+static bool audio_mixeng_backend_set_dbus_server(AudioBackend *be,\n+                                                 GDBusObjectManagerServer *manager,\n+                                                 bool p2p,\n+                                                 Error **errp)\n+{\n+    AudioMixengBackend *d = AUDIO_MIXENG_BACKEND(be);\n+\n+    if (!d->drv->set_dbus_server) {\n+        return false;\n+    }\n+\n+    return d->drv->set_dbus_server(be, manager, p2p, errp);\n+}\n+\n+#endif\n+\n+static void audio_mixeng_backend_class_init(ObjectClass *klass, const void *data)\n+{\n+    AudioBackendClass *be = AUDIO_BACKEND_CLASS(klass);\n+\n+    be->get_id = audio_mixeng_backend_get_id;\n+#ifdef CONFIG_GIO\n+    be->set_dbus_server = audio_mixeng_backend_set_dbus_server;\n+#endif\n+}\n+\n+static void audio_mixeng_backend_init(Object *obj)\n+{\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n \n     QLIST_INIT(&s->hw_head_out);\n     QLIST_INIT(&s->hw_head_in);\n@@ -1629,9 +1670,9 @@ static void audio_be_init(Object *obj)\n     vmstate_register_any(NULL, &vmstate_audio, s);\n }\n \n-static void audio_be_finalize(Object *obj)\n+static void audio_mixeng_backend_finalize(Object *obj)\n {\n-    AudioBackend *s = AUDIO_BACKEND(obj);\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n     HWVoiceOut *hwo, *hwon;\n     HWVoiceIn *hwi, *hwin;\n \n@@ -1747,10 +1788,10 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)\n {\n     int done = 0;\n     const char *drvname;\n-    AudioBackend *s;\n+    AudioMixengBackend *s;\n     struct audio_driver *driver;\n \n-    s = AUDIO_BACKEND(object_new(TYPE_AUDIO_BACKEND));\n+    s = AUDIO_MIXENG_BACKEND(object_new(TYPE_AUDIO_MIXENG_BACKEND));\n \n     if (dev) {\n         /* -audiodev option */\n@@ -1758,7 +1799,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)\n         drvname = AudiodevDriver_str(dev->driver);\n         driver = audio_driver_lookup(drvname);\n         if (driver) {\n-            done = audio_driver_init(s, driver, dev, errp);\n+            done = audio_driver_do_init(s, driver, dev, errp);\n         } else {\n             error_setg(errp, \"Unknown audio driver `%s'\", drvname);\n         }\n@@ -1778,7 +1819,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)\n             g_free(e);\n             drvname = AudiodevDriver_str(dev->driver);\n             driver = audio_driver_lookup(drvname);\n-            if (audio_driver_init(s, driver, dev, NULL)) {\n+            if (audio_driver_do_init(s, driver, dev, NULL)) {\n                 break;\n             }\n             qapi_free_Audiodev(dev);\n@@ -1790,7 +1831,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)\n         goto out;\n     }\n     object_unref(s);\n-    return s;\n+    return AUDIO_BACKEND(s);\n \n out:\n     object_unref(s);\n@@ -1829,12 +1870,13 @@ bool AUD_backend_check(AudioBackend **be, Error **errp)\n static struct audio_pcm_ops capture_pcm_ops;\n \n CaptureVoiceOut *AUD_add_capture(\n-    AudioBackend *s,\n+    AudioBackend *be,\n     struct audsettings *as,\n     struct audio_capture_ops *ops,\n     void *cb_opaque\n     )\n {\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(be);\n     CaptureVoiceOut *cap;\n     struct capture_callback *cb;\n \n@@ -2225,27 +2267,37 @@ AudioBackend *audio_be_by_name(const char *name, Error **errp)\n }\n \n #ifdef CONFIG_GIO\n+bool audio_be_can_set_dbus_server(AudioBackend *be)\n+{\n+    /*\n+     * TODO:\n+     * AudioBackendClass *klass = AUDIO_BACKEND_GET_CLASS(be);\n+     * return klass->set_dbus_server != NULL;\n+     */\n+    return AUDIO_MIXENG_BACKEND(be)->drv->set_dbus_server != NULL;\n+}\n+\n bool audio_be_set_dbus_server(AudioBackend *be,\n                               GDBusObjectManagerServer *server,\n                               bool p2p,\n                               Error **errp)\n {\n-    assert(be != NULL);\n+    AudioBackendClass *klass = AUDIO_BACKEND_GET_CLASS(be);\n \n-    if (!be->drv->set_dbus_server) {\n-        error_setg(errp, \"Audiodev '%s' is not compatible with DBus\", be->dev->id);\n+    if (!audio_be_can_set_dbus_server(be)) {\n+        error_setg(errp, \"Audiodev '%s' is not compatible with DBus\",\n+                   audio_be_get_id(be));\n         return false;\n     }\n \n-    return be->drv->set_dbus_server(be, server, p2p, errp);\n+    return klass->set_dbus_server(be, server, p2p, errp);\n }\n #endif\n \n const char *audio_be_get_id(AudioBackend *be)\n {\n     if (be) {\n-        assert(be->dev);\n-        return be->dev->id;\n+        return AUDIO_BACKEND_GET_CLASS(be)->get_id(be);\n     } else {\n         return \"\";\n     }\n@@ -2320,13 +2372,24 @@ static const TypeInfo audio_be_info = {\n     .instance_size = sizeof(AudioBackend),\n     .instance_init = audio_be_init,\n     .instance_finalize = audio_be_finalize,\n-    .abstract = false, /* TODO: subclass drivers and make it abstract */\n+    .abstract = true,\n     .class_size = sizeof(AudioBackendClass),\n };\n \n+static const TypeInfo audio_mixeng_backend_info = {\n+    .name = TYPE_AUDIO_MIXENG_BACKEND,\n+    .parent = TYPE_AUDIO_BACKEND,\n+    .instance_size = sizeof(AudioMixengBackend),\n+    .instance_init = audio_mixeng_backend_init,\n+    .instance_finalize = audio_mixeng_backend_finalize,\n+    .class_size = sizeof(AudioMixengBackendClass),\n+    .class_init = audio_mixeng_backend_class_init,\n+};\n+\n static void register_types(void)\n {\n     type_register_static(&audio_be_info);\n+    type_register_static(&audio_mixeng_backend_info);\n }\n \n type_init(register_types);\ndiff --git a/audio/dbusaudio.c b/audio/dbusaudio.c\nindex 1fe7c4ed647..96d4fa3f471 100644\n--- a/audio/dbusaudio.c\n+++ b/audio/dbusaudio.c\n@@ -464,7 +464,7 @@ listener_in_vanished_cb(GDBusConnection *connection,\n }\n \n static gboolean\n-dbus_audio_register_listener(AudioBackend *s,\n+dbus_audio_register_listener(AudioMixengBackend *s,\n                              GDBusMethodInvocation *invocation,\n #ifdef G_OS_UNIX\n                              GUnixFDList *fd_list,\n@@ -621,7 +621,7 @@ dbus_audio_register_listener(AudioBackend *s,\n }\n \n static gboolean\n-dbus_audio_register_out_listener(AudioBackend *s,\n+dbus_audio_register_out_listener(AudioMixengBackend *s,\n                                  GDBusMethodInvocation *invocation,\n #ifdef G_OS_UNIX\n                                  GUnixFDList *fd_list,\n@@ -637,7 +637,7 @@ dbus_audio_register_out_listener(AudioBackend *s,\n }\n \n static gboolean\n-dbus_audio_register_in_listener(AudioBackend *s,\n+dbus_audio_register_in_listener(AudioMixengBackend *s,\n                                 GDBusMethodInvocation *invocation,\n #ifdef G_OS_UNIX\n                                 GUnixFDList *fd_list,\n@@ -657,7 +657,7 @@ dbus_audio_set_server(AudioBackend *s,\n                       bool p2p,\n                       Error **errp)\n {\n-    DBusAudio *da = s->drv_opaque;\n+    DBusAudio *da = AUDIO_MIXENG_BACKEND(s)->drv_opaque;\n \n     g_assert(da);\n     g_assert(!da->server);\ndiff --git a/audio/ossaudio.c b/audio/ossaudio.c\nindex c6cad47a015..6ad20ab1876 100644\n--- a/audio/ossaudio.c\n+++ b/audio/ossaudio.c\n@@ -107,13 +107,13 @@ static void oss_anal_close (int *fdp)\n \n static void oss_helper_poll_out (void *opaque)\n {\n-    AudioBackend *s = opaque;\n+    AudioMixengBackend *s = opaque;\n     audio_run(s, \"oss_poll_out\");\n }\n \n static void oss_helper_poll_in (void *opaque)\n {\n-    AudioBackend *s = opaque;\n+    AudioMixengBackend *s = opaque;\n     audio_run(s, \"oss_poll_in\");\n }\n \ndiff --git a/tests/audio/test-audio.c b/tests/audio/test-audio.c\nindex edb4971972c..66693f0fbfd 100644\n--- a/tests/audio/test-audio.c\n+++ b/tests/audio/test-audio.c\n@@ -170,7 +170,7 @@ static void test_audio_out_sine_wave(void)\n      */\n     start_time = g_get_monotonic_time();\n     while (state.frames_written < state.total_frames) {\n-        audio_run(state.be, \"test\");\n+        audio_run(AUDIO_MIXENG_BACKEND(state.be), \"test\");\n         main_loop_wait(true);\n \n         elapsed_ms = (g_get_monotonic_time() - start_time) / 1000;\n@@ -432,7 +432,7 @@ static void test_audio_capture(void)\n     start_time = g_get_monotonic_time();\n     while (sine_state.frames_written < sine_state.total_frames ||\n            state.captured_bytes < CAPTURE_BUFFER_SIZE) {\n-        audio_run(be, \"test-capture\");\n+        audio_run(AUDIO_MIXENG_BACKEND(be), \"test-capture\");\n         main_loop_wait(true);\n \n         elapsed_ms = (g_get_monotonic_time() - start_time) / 1000;\ndiff --git a/ui/dbus.c b/ui/dbus.c\nindex 0be1931d208..905ee6fea71 100644\n--- a/ui/dbus.c\n+++ b/ui/dbus.c\n@@ -221,8 +221,7 @@ dbus_display_complete(UserCreatable *uc, Error **errp)\n \n     {\n         AudioBackend *audio_be = audio_get_default_audio_be(NULL);\n-\n-        if (audio_be && !g_str_equal(audio_be->drv->name, \"dbus\")) {\n+        if (audio_be && !audio_be_can_set_dbus_server(audio_be)) {\n             audio_be = NULL;\n         }\n         if (dd->audiodev && *dd->audiodev) {\n@@ -231,8 +230,7 @@ dbus_display_complete(UserCreatable *uc, Error **errp)\n                 return;\n             }\n         }\n-        if (audio_be &&\n-            !audio_be_set_dbus_server(audio_be, dd->server, dd->p2p, errp)) {\n+        if (audio_be && !audio_be_set_dbus_server(audio_be, dd->server, dd->p2p, errp)) {\n             return;\n         }\n     }\n",
    "prefixes": [
        "20/85"
    ]
}