audio/pa: convert to QOM lifecycle methods
Migrate the PulseAudio backend from the legacy driver init/fini callbacks to proper QOM realize and finalize methods. The paaudio struct fields are now embedded directly in the AudioPa QOM object instead of being allocated separately as drv_opaque. This allows accessing the backend state through proper QOM type casting with AUDIO_PA() rather than casting drv_opaque pointers. The PulseAudio connection is now managed through the QOM lifecycle, with the connection reference acquired in realize and released in finalize. Reviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Marc-André Lureau committed
Jan 16, 2026 at 18:22 UTC
5bb7016727ac5fcc91efa141d411cafc9dfe7b1e
1 file changed
+57
-52
audio/paaudio.c
+57
-52
@@ -14,19 +14,10 @@
14
#define TYPE_AUDIO_PA "audio-pa"
15
OBJECT_DECLARE_SIMPLE_TYPE(AudioPa, AUDIO_PA)
16
17
-struct AudioPa {
18
- AudioMixengBackend parent_obj;
19
-};
17
+static AudioBackendClass *audio_pa_parent_class;
18
19
static struct audio_driver pa_audio_driver;
20
23
-static void audio_pa_class_init(ObjectClass *klass, const void *data)
24
-{
25
- AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
26
-
27
- k->driver = &pa_audio_driver;
28
-}
29
-
21
typedef struct PAConnection {
22
char *server;
23
int refcount;
@@ -36,18 +27,19 @@ typedef struct PAConnection {
27
pa_context *context;
28
} PAConnection;
29
39
-static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
40
- QTAILQ_HEAD_INITIALIZER(pa_conns);
30
+struct AudioPa {
31
+ AudioMixengBackend parent_obj;
32
42
-typedef struct {
43
- Audiodev *dev;
33
PAConnection *conn;
45
-} paaudio;
34
+};
35
+
36
+static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
37
+ QTAILQ_HEAD_INITIALIZER(pa_conns);
38
39
typedef struct {
40
HWVoiceOut hw;
41
pa_stream *stream;
50
- paaudio *g;
42
+ AudioPa *g;
43
} PAVoiceOut;
44
45
typedef struct {
@@ -55,7 +47,7 @@ typedef struct {
47
pa_stream *stream;
48
const void *read_data;
49
size_t read_length;
58
- paaudio *g;
50
+ AudioPa *g;
51
} PAVoiceIn;
52
53
static void qpa_conn_fini(PAConnection *c);
@@ -529,23 +521,25 @@ fail:
521
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
522
void *drv_opaque)
523
{
524
+ AudioMixengBackend *amb = hw->s;
525
+ AudioPa *apa = AUDIO_PA(amb);
526
int error;
527
pa_sample_spec ss;
528
pa_buffer_attr ba;
529
struct audsettings obt_as = *as;
530
PAVoiceOut *pa = (PAVoiceOut *) hw;
537
- paaudio *g = pa->g = drv_opaque;
538
- AudiodevPaOptions *popts = &g->dev->u.pa;
531
+ AudiodevPaOptions *popts = &amb->dev->u.pa;
532
AudiodevPaPerDirectionOptions *ppdo = popts->out;
540
- PAConnection *c = g->conn;
533
+ PAConnection *c = apa->conn;
534
535
+ pa->g = apa;
536
ss.format = audfmt_to_pa (as->fmt, as->endianness);
537
ss.channels = as->nchannels;
538
ss.rate = as->freq;
539
540
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
541
ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
548
- (g->dev->timer_period >> 2) * 3), &ss);
542
+ (amb->dev->timer_period >> 2) * 3), &ss);
543
ba.maxlength = -1;
544
ba.prebuf = -1;
545
@@ -553,7 +547,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
547
548
pa->stream = qpa_simple_new (
549
c,
556
- ppdo->stream_name ?: g->dev->id,
550
+ ppdo->stream_name ?: amb->dev->id,
551
PA_STREAM_PLAYBACK,
552
ppdo->name,
553
&ss,
@@ -578,23 +572,25 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
572
573
static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
574
{
575
+ AudioMixengBackend *amb = hw->s;
576
+ AudioPa *apa = AUDIO_PA(amb);
577
int error;
578
pa_sample_spec ss;
579
pa_buffer_attr ba;
580
struct audsettings obt_as = *as;
581
PAVoiceIn *pa = (PAVoiceIn *) hw;
586
- paaudio *g = pa->g = drv_opaque;
587
- AudiodevPaOptions *popts = &g->dev->u.pa;
582
+ AudiodevPaOptions *popts = &amb->dev->u.pa;
583
AudiodevPaPerDirectionOptions *ppdo = popts->in;
589
- PAConnection *c = g->conn;
584
+ PAConnection *c = apa->conn;
585
586
+ pa->g = apa;
587
ss.format = audfmt_to_pa (as->fmt, as->endianness);
588
ss.channels = as->nchannels;
589
ss.rate = as->freq;
590
595
- ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
591
+ ba.fragsize = pa_usec_to_bytes((amb->dev->timer_period >> 1) * 3, &ss);
592
ba.maxlength = pa_usec_to_bytes(
597
- MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
593
+ MAX(ppdo->latency, amb->dev->timer_period * 3), &ss);
594
ba.minreq = -1;
595
ba.prebuf = -1;
596
@@ -602,7 +598,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
598
599
pa->stream = qpa_simple_new (
600
c,
605
- ppdo->stream_name ?: g->dev->id,
601
+ ppdo->stream_name ?: amb->dev->id,
602
PA_STREAM_RECORD,
603
ppdo->name,
604
&ss,
@@ -834,15 +830,20 @@ fail:
830
return NULL;
831
}
832
837
-static void *qpa_audio_init(Audiodev *dev, Error **errp)
833
+static bool
834
+audio_pa_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
835
{
839
- paaudio *g;
836
+ AudioPa *apa = AUDIO_PA(abe);
837
AudiodevPaOptions *popts = &dev->u.pa;
838
const char *server;
839
PAConnection *c;
840
841
assert(dev->driver == AUDIODEV_DRIVER_PA);
842
843
+ if (!audio_pa_parent_class->realize(abe, dev, errp)) {
844
+ return false;
845
+ }
846
+
847
if (!popts->server) {
848
char pidfile[64];
849
char *runtime;
@@ -851,42 +852,38 @@ static void *qpa_audio_init(Audiodev *dev, Error **errp)
852
runtime = getenv("XDG_RUNTIME_DIR");
853
if (!runtime) {
854
error_setg(errp, "XDG_RUNTIME_DIR not set");
854
- return NULL;
855
+ return false;
856
}
857
snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
858
if (stat(pidfile, &st) != 0) {
859
error_setg_errno(errp, errno, "could not stat pidfile %s", pidfile);
859
- return NULL;
860
+ return false;
861
}
862
}
863
864
qpa_validate_per_direction_opts(dev, popts->in);
865
qpa_validate_per_direction_opts(dev, popts->out);
866
866
- g = g_new0(paaudio, 1);
867
server = popts->server;
868
-
869
- g->dev = dev;
870
-
868
QTAILQ_FOREACH(c, &pa_conns, list) {
869
if (server == NULL || c->server == NULL ?
870
server == c->server :
871
strcmp(server, c->server) == 0) {
875
- g->conn = c;
872
+ apa->conn = c;
873
break;
874
}
875
}
879
- if (!g->conn) {
880
- g->conn = qpa_conn_init(server);
876
+ if (!apa->conn) {
877
+ apa->conn = qpa_conn_init(server);
878
}
882
- if (!g->conn) {
883
- g_free(g);
879
+ if (!apa->conn) {
880
error_setg(errp, "could not connect to PulseAudio server");
885
- return NULL;
881
+ return false;
882
}
883
888
- ++g->conn->refcount;
889
- return g;
884
+ ++apa->conn->refcount;
885
+
886
+ return true;
887
}
888
889
static void qpa_conn_fini(PAConnection *c)
@@ -908,16 +905,14 @@ static void qpa_conn_fini(PAConnection *c)
905
g_free(c);
906
}
907
911
-static void qpa_audio_fini (void *opaque)
908
+static void audio_pa_finalize(Object *obj)
909
{
913
- paaudio *g = opaque;
914
- PAConnection *c = g->conn;
910
+ AudioPa *apa = AUDIO_PA(obj);
911
+ PAConnection *c = apa->conn;
912
916
- if (--c->refcount == 0) {
913
+ if (c && --c->refcount == 0) {
914
qpa_conn_fini(c);
915
}
919
-
920
- g_free(g);
916
}
917
918
static struct audio_pcm_ops qpa_pcm_ops = {
@@ -939,8 +934,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
934
935
static struct audio_driver pa_audio_driver = {
936
.name = "pa",
942
- .init = qpa_audio_init,
943
- .fini = qpa_audio_fini,
937
.pcm_ops = &qpa_pcm_ops,
938
.max_voices_out = INT_MAX,
939
.max_voices_in = INT_MAX,
@@ -948,12 +941,24 @@ static struct audio_driver pa_audio_driver = {
941
.voice_size_in = sizeof (PAVoiceIn),
942
};
943
944
+static void audio_pa_class_init(ObjectClass *klass, const void *data)
945
+{
946
+ AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
947
+ AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
948
+
949
+ audio_pa_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
950
+
951
+ b->realize = audio_pa_realize;
952
+ k->driver = &pa_audio_driver;
953
+}
954
+
955
static const TypeInfo audio_types[] = {
956
{
957
.name = TYPE_AUDIO_PA,
958
.parent = TYPE_AUDIO_MIXENG_BACKEND,
959
.instance_size = sizeof(AudioPa),
960
.class_init = audio_pa_class_init,
961
+ .instance_finalize = audio_pa_finalize,
962
},
963
};
964