xref: /openbmc/qemu/audio/paaudio.c (revision 00e076795f2d6dfa0c078ff5d5ee5d77190cb4b9)
1b8e59f18Smalc /* public domain */
2b8e59f18Smalc #include "qemu-common.h"
3b8e59f18Smalc #include "audio.h"
4b8e59f18Smalc 
5b8e59f18Smalc #include <pulse/simple.h>
6b8e59f18Smalc #include <pulse/error.h>
7b8e59f18Smalc 
8b8e59f18Smalc #define AUDIO_CAP "pulseaudio"
9b8e59f18Smalc #include "audio_int.h"
10b8e59f18Smalc #include "audio_pt_int.h"
11b8e59f18Smalc 
12b8e59f18Smalc typedef struct {
13b8e59f18Smalc     HWVoiceOut hw;
14b8e59f18Smalc     int done;
15b8e59f18Smalc     int live;
16b8e59f18Smalc     int decr;
17b8e59f18Smalc     int rpos;
18b8e59f18Smalc     pa_simple *s;
19b8e59f18Smalc     void *pcm_buf;
20b8e59f18Smalc     struct audio_pt pt;
21b8e59f18Smalc } PAVoiceOut;
22b8e59f18Smalc 
23b8e59f18Smalc typedef struct {
24b8e59f18Smalc     HWVoiceIn hw;
25b8e59f18Smalc     int done;
26b8e59f18Smalc     int dead;
27b8e59f18Smalc     int incr;
28b8e59f18Smalc     int wpos;
29b8e59f18Smalc     pa_simple *s;
30b8e59f18Smalc     void *pcm_buf;
31b8e59f18Smalc     struct audio_pt pt;
32b8e59f18Smalc } PAVoiceIn;
33b8e59f18Smalc 
34b8e59f18Smalc static struct {
35b8e59f18Smalc     int samples;
36b8e59f18Smalc     int divisor;
37b8e59f18Smalc     char *server;
38b8e59f18Smalc     char *sink;
39b8e59f18Smalc     char *source;
40b8e59f18Smalc } conf = {
411a40d5e2SJuan Quintela     .samples = 1024,
421a40d5e2SJuan Quintela     .divisor = 2,
43b8e59f18Smalc };
44b8e59f18Smalc 
45b8e59f18Smalc static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
46b8e59f18Smalc {
47b8e59f18Smalc     va_list ap;
48b8e59f18Smalc 
49b8e59f18Smalc     va_start (ap, fmt);
50b8e59f18Smalc     AUD_vlog (AUDIO_CAP, fmt, ap);
51b8e59f18Smalc     va_end (ap);
52b8e59f18Smalc 
53b8e59f18Smalc     AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
54b8e59f18Smalc }
55b8e59f18Smalc 
56b8e59f18Smalc static void *qpa_thread_out (void *arg)
57b8e59f18Smalc {
58b8e59f18Smalc     PAVoiceOut *pa = arg;
59b8e59f18Smalc     HWVoiceOut *hw = &pa->hw;
60b8e59f18Smalc     int threshold;
61b8e59f18Smalc 
62b8e59f18Smalc     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
63b8e59f18Smalc 
64b8e59f18Smalc     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
65b8e59f18Smalc         return NULL;
66b8e59f18Smalc     }
67b8e59f18Smalc 
68b8e59f18Smalc     for (;;) {
69b8e59f18Smalc         int decr, to_mix, rpos;
70b8e59f18Smalc 
71b8e59f18Smalc         for (;;) {
72b8e59f18Smalc             if (pa->done) {
73b8e59f18Smalc                 goto exit;
74b8e59f18Smalc             }
75b8e59f18Smalc 
76b8e59f18Smalc             if (pa->live > threshold) {
77b8e59f18Smalc                 break;
78b8e59f18Smalc             }
79b8e59f18Smalc 
80b8e59f18Smalc             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
81b8e59f18Smalc                 goto exit;
82b8e59f18Smalc             }
83b8e59f18Smalc         }
84b8e59f18Smalc 
85b8e59f18Smalc         decr = to_mix = pa->live;
86b8e59f18Smalc         rpos = hw->rpos;
87b8e59f18Smalc 
88b8e59f18Smalc         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
89b8e59f18Smalc             return NULL;
90b8e59f18Smalc         }
91b8e59f18Smalc 
92b8e59f18Smalc         while (to_mix) {
93b8e59f18Smalc             int error;
94b8e59f18Smalc             int chunk = audio_MIN (to_mix, hw->samples - rpos);
951ea879e5Smalc             struct st_sample *src = hw->mix_buf + rpos;
96b8e59f18Smalc 
97b8e59f18Smalc             hw->clip (pa->pcm_buf, src, chunk);
98b8e59f18Smalc 
99b8e59f18Smalc             if (pa_simple_write (pa->s, pa->pcm_buf,
100b8e59f18Smalc                                  chunk << hw->info.shift, &error) < 0) {
101b8e59f18Smalc                 qpa_logerr (error, "pa_simple_write failed\n");
102b8e59f18Smalc                 return NULL;
103b8e59f18Smalc             }
104b8e59f18Smalc 
105b8e59f18Smalc             rpos = (rpos + chunk) % hw->samples;
106b8e59f18Smalc             to_mix -= chunk;
107b8e59f18Smalc         }
108b8e59f18Smalc 
109b8e59f18Smalc         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
110b8e59f18Smalc             return NULL;
111b8e59f18Smalc         }
112b8e59f18Smalc 
113fd5723b3SWu Fengguang         pa->live = 0;
114b8e59f18Smalc         pa->rpos = rpos;
115b8e59f18Smalc         pa->decr += decr;
116b8e59f18Smalc     }
117b8e59f18Smalc 
118b8e59f18Smalc  exit:
119b8e59f18Smalc     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
120b8e59f18Smalc     return NULL;
121b8e59f18Smalc }
122b8e59f18Smalc 
123bdff253cSmalc static int qpa_run_out (HWVoiceOut *hw, int live)
124b8e59f18Smalc {
125bdff253cSmalc     int decr;
126b8e59f18Smalc     PAVoiceOut *pa = (PAVoiceOut *) hw;
127b8e59f18Smalc 
128b8e59f18Smalc     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
129b8e59f18Smalc         return 0;
130b8e59f18Smalc     }
131b8e59f18Smalc 
132b8e59f18Smalc     decr = audio_MIN (live, pa->decr);
133b8e59f18Smalc     pa->decr -= decr;
134b8e59f18Smalc     pa->live = live - decr;
135b8e59f18Smalc     hw->rpos = pa->rpos;
136b8e59f18Smalc     if (pa->live > 0) {
137b8e59f18Smalc         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
138b8e59f18Smalc     }
139b8e59f18Smalc     else {
140b8e59f18Smalc         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
141b8e59f18Smalc     }
142b8e59f18Smalc     return decr;
143b8e59f18Smalc }
144b8e59f18Smalc 
145b8e59f18Smalc static int qpa_write (SWVoiceOut *sw, void *buf, int len)
146b8e59f18Smalc {
147b8e59f18Smalc     return audio_pcm_sw_write (sw, buf, len);
148b8e59f18Smalc }
149b8e59f18Smalc 
150b8e59f18Smalc /* capture */
151b8e59f18Smalc static void *qpa_thread_in (void *arg)
152b8e59f18Smalc {
153b8e59f18Smalc     PAVoiceIn *pa = arg;
154b8e59f18Smalc     HWVoiceIn *hw = &pa->hw;
155b8e59f18Smalc     int threshold;
156b8e59f18Smalc 
157b8e59f18Smalc     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
158b8e59f18Smalc 
159b8e59f18Smalc     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
160b8e59f18Smalc         return NULL;
161b8e59f18Smalc     }
162b8e59f18Smalc 
163b8e59f18Smalc     for (;;) {
164b8e59f18Smalc         int incr, to_grab, wpos;
165b8e59f18Smalc 
166b8e59f18Smalc         for (;;) {
167b8e59f18Smalc             if (pa->done) {
168b8e59f18Smalc                 goto exit;
169b8e59f18Smalc             }
170b8e59f18Smalc 
171b8e59f18Smalc             if (pa->dead > threshold) {
172b8e59f18Smalc                 break;
173b8e59f18Smalc             }
174b8e59f18Smalc 
175b8e59f18Smalc             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
176b8e59f18Smalc                 goto exit;
177b8e59f18Smalc             }
178b8e59f18Smalc         }
179b8e59f18Smalc 
180b8e59f18Smalc         incr = to_grab = pa->dead;
181b8e59f18Smalc         wpos = hw->wpos;
182b8e59f18Smalc 
183b8e59f18Smalc         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
184b8e59f18Smalc             return NULL;
185b8e59f18Smalc         }
186b8e59f18Smalc 
187b8e59f18Smalc         while (to_grab) {
188b8e59f18Smalc             int error;
189b8e59f18Smalc             int chunk = audio_MIN (to_grab, hw->samples - wpos);
190b8e59f18Smalc             void *buf = advance (pa->pcm_buf, wpos);
191b8e59f18Smalc 
192b8e59f18Smalc             if (pa_simple_read (pa->s, buf,
193b8e59f18Smalc                                 chunk << hw->info.shift, &error) < 0) {
194b8e59f18Smalc                 qpa_logerr (error, "pa_simple_read failed\n");
195b8e59f18Smalc                 return NULL;
196b8e59f18Smalc             }
197b8e59f18Smalc 
198*00e07679SMichael Walle             hw->conv (hw->conv_buf + wpos, buf, chunk);
199b8e59f18Smalc             wpos = (wpos + chunk) % hw->samples;
200b8e59f18Smalc             to_grab -= chunk;
201b8e59f18Smalc         }
202b8e59f18Smalc 
203b8e59f18Smalc         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
204b8e59f18Smalc             return NULL;
205b8e59f18Smalc         }
206b8e59f18Smalc 
207b8e59f18Smalc         pa->wpos = wpos;
208b8e59f18Smalc         pa->dead -= incr;
209b8e59f18Smalc         pa->incr += incr;
210b8e59f18Smalc     }
211b8e59f18Smalc 
212b8e59f18Smalc  exit:
213b8e59f18Smalc     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
214b8e59f18Smalc     return NULL;
215b8e59f18Smalc }
216b8e59f18Smalc 
217b8e59f18Smalc static int qpa_run_in (HWVoiceIn *hw)
218b8e59f18Smalc {
219b8e59f18Smalc     int live, incr, dead;
220b8e59f18Smalc     PAVoiceIn *pa = (PAVoiceIn *) hw;
221b8e59f18Smalc 
222b8e59f18Smalc     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
223b8e59f18Smalc         return 0;
224b8e59f18Smalc     }
225b8e59f18Smalc 
226b8e59f18Smalc     live = audio_pcm_hw_get_live_in (hw);
227b8e59f18Smalc     dead = hw->samples - live;
228b8e59f18Smalc     incr = audio_MIN (dead, pa->incr);
229b8e59f18Smalc     pa->incr -= incr;
230b8e59f18Smalc     pa->dead = dead - incr;
231b8e59f18Smalc     hw->wpos = pa->wpos;
232b8e59f18Smalc     if (pa->dead > 0) {
233b8e59f18Smalc         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
234b8e59f18Smalc     }
235b8e59f18Smalc     else {
236b8e59f18Smalc         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
237b8e59f18Smalc     }
238b8e59f18Smalc     return incr;
239b8e59f18Smalc }
240b8e59f18Smalc 
241b8e59f18Smalc static int qpa_read (SWVoiceIn *sw, void *buf, int len)
242b8e59f18Smalc {
243b8e59f18Smalc     return audio_pcm_sw_read (sw, buf, len);
244b8e59f18Smalc }
245b8e59f18Smalc 
246b8e59f18Smalc static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
247b8e59f18Smalc {
248b8e59f18Smalc     int format;
249b8e59f18Smalc 
250b8e59f18Smalc     switch (afmt) {
251b8e59f18Smalc     case AUD_FMT_S8:
252b8e59f18Smalc     case AUD_FMT_U8:
253b8e59f18Smalc         format = PA_SAMPLE_U8;
254b8e59f18Smalc         break;
255b8e59f18Smalc     case AUD_FMT_S16:
256b8e59f18Smalc     case AUD_FMT_U16:
257b8e59f18Smalc         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
258b8e59f18Smalc         break;
259b8e59f18Smalc     case AUD_FMT_S32:
260b8e59f18Smalc     case AUD_FMT_U32:
261b8e59f18Smalc         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
262b8e59f18Smalc         break;
263b8e59f18Smalc     default:
264b8e59f18Smalc         dolog ("Internal logic error: Bad audio format %d\n", afmt);
265b8e59f18Smalc         format = PA_SAMPLE_U8;
266b8e59f18Smalc         break;
267b8e59f18Smalc     }
268b8e59f18Smalc     return format;
269b8e59f18Smalc }
270b8e59f18Smalc 
271b8e59f18Smalc static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
272b8e59f18Smalc {
273b8e59f18Smalc     switch (fmt) {
274b8e59f18Smalc     case PA_SAMPLE_U8:
275b8e59f18Smalc         return AUD_FMT_U8;
276b8e59f18Smalc     case PA_SAMPLE_S16BE:
277b8e59f18Smalc         *endianness = 1;
278b8e59f18Smalc         return AUD_FMT_S16;
279b8e59f18Smalc     case PA_SAMPLE_S16LE:
280b8e59f18Smalc         *endianness = 0;
281b8e59f18Smalc         return AUD_FMT_S16;
282b8e59f18Smalc     case PA_SAMPLE_S32BE:
283b8e59f18Smalc         *endianness = 1;
284b8e59f18Smalc         return AUD_FMT_S32;
285b8e59f18Smalc     case PA_SAMPLE_S32LE:
286b8e59f18Smalc         *endianness = 0;
287b8e59f18Smalc         return AUD_FMT_S32;
288b8e59f18Smalc     default:
289b8e59f18Smalc         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
290b8e59f18Smalc         return AUD_FMT_U8;
291b8e59f18Smalc     }
292b8e59f18Smalc }
293b8e59f18Smalc 
2941ea879e5Smalc static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
295b8e59f18Smalc {
296b8e59f18Smalc     int error;
297b8e59f18Smalc     static pa_sample_spec ss;
2981ea879e5Smalc     struct audsettings obt_as = *as;
299b8e59f18Smalc     PAVoiceOut *pa = (PAVoiceOut *) hw;
300b8e59f18Smalc 
301b8e59f18Smalc     ss.format = audfmt_to_pa (as->fmt, as->endianness);
302b8e59f18Smalc     ss.channels = as->nchannels;
303b8e59f18Smalc     ss.rate = as->freq;
304b8e59f18Smalc 
305b8e59f18Smalc     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
306b8e59f18Smalc 
307b8e59f18Smalc     pa->s = pa_simple_new (
308b8e59f18Smalc         conf.server,
309b8e59f18Smalc         "qemu",
310b8e59f18Smalc         PA_STREAM_PLAYBACK,
311b8e59f18Smalc         conf.sink,
312b8e59f18Smalc         "pcm.playback",
313b8e59f18Smalc         &ss,
314b8e59f18Smalc         NULL,                   /* channel map */
315b8e59f18Smalc         NULL,                   /* buffering attributes */
316b8e59f18Smalc         &error
317b8e59f18Smalc         );
318b8e59f18Smalc     if (!pa->s) {
319b8e59f18Smalc         qpa_logerr (error, "pa_simple_new for playback failed\n");
320b8e59f18Smalc         goto fail1;
321b8e59f18Smalc     }
322b8e59f18Smalc 
323b8e59f18Smalc     audio_pcm_init_info (&hw->info, &obt_as);
324b8e59f18Smalc     hw->samples = conf.samples;
325b8e59f18Smalc     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
326b8e59f18Smalc     if (!pa->pcm_buf) {
327b8e59f18Smalc         dolog ("Could not allocate buffer (%d bytes)\n",
328b8e59f18Smalc                hw->samples << hw->info.shift);
329b8e59f18Smalc         goto fail2;
330b8e59f18Smalc     }
331b8e59f18Smalc 
332b8e59f18Smalc     if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
333b8e59f18Smalc         goto fail3;
334b8e59f18Smalc     }
335b8e59f18Smalc 
336b8e59f18Smalc     return 0;
337b8e59f18Smalc 
338b8e59f18Smalc  fail3:
3395d928867SJean-Christophe Dubois     qemu_free (pa->pcm_buf);
340b8e59f18Smalc     pa->pcm_buf = NULL;
341b8e59f18Smalc  fail2:
342b8e59f18Smalc     pa_simple_free (pa->s);
343b8e59f18Smalc     pa->s = NULL;
344b8e59f18Smalc  fail1:
345b8e59f18Smalc     return -1;
346b8e59f18Smalc }
347b8e59f18Smalc 
3481ea879e5Smalc static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
349b8e59f18Smalc {
350b8e59f18Smalc     int error;
351b8e59f18Smalc     static pa_sample_spec ss;
3521ea879e5Smalc     struct audsettings obt_as = *as;
353b8e59f18Smalc     PAVoiceIn *pa = (PAVoiceIn *) hw;
354b8e59f18Smalc 
355b8e59f18Smalc     ss.format = audfmt_to_pa (as->fmt, as->endianness);
356b8e59f18Smalc     ss.channels = as->nchannels;
357b8e59f18Smalc     ss.rate = as->freq;
358b8e59f18Smalc 
359b8e59f18Smalc     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
360b8e59f18Smalc 
361b8e59f18Smalc     pa->s = pa_simple_new (
362b8e59f18Smalc         conf.server,
363b8e59f18Smalc         "qemu",
364b8e59f18Smalc         PA_STREAM_RECORD,
365b8e59f18Smalc         conf.source,
366b8e59f18Smalc         "pcm.capture",
367b8e59f18Smalc         &ss,
368b8e59f18Smalc         NULL,                   /* channel map */
369b8e59f18Smalc         NULL,                   /* buffering attributes */
370b8e59f18Smalc         &error
371b8e59f18Smalc         );
372b8e59f18Smalc     if (!pa->s) {
373b8e59f18Smalc         qpa_logerr (error, "pa_simple_new for capture failed\n");
374b8e59f18Smalc         goto fail1;
375b8e59f18Smalc     }
376b8e59f18Smalc 
377b8e59f18Smalc     audio_pcm_init_info (&hw->info, &obt_as);
378b8e59f18Smalc     hw->samples = conf.samples;
379b8e59f18Smalc     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
380b8e59f18Smalc     if (!pa->pcm_buf) {
381b8e59f18Smalc         dolog ("Could not allocate buffer (%d bytes)\n",
382b8e59f18Smalc                hw->samples << hw->info.shift);
383b8e59f18Smalc         goto fail2;
384b8e59f18Smalc     }
385b8e59f18Smalc 
386b8e59f18Smalc     if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
387b8e59f18Smalc         goto fail3;
388b8e59f18Smalc     }
389b8e59f18Smalc 
390b8e59f18Smalc     return 0;
391b8e59f18Smalc 
392b8e59f18Smalc  fail3:
3935d928867SJean-Christophe Dubois     qemu_free (pa->pcm_buf);
394b8e59f18Smalc     pa->pcm_buf = NULL;
395b8e59f18Smalc  fail2:
396b8e59f18Smalc     pa_simple_free (pa->s);
397b8e59f18Smalc     pa->s = NULL;
398b8e59f18Smalc  fail1:
399b8e59f18Smalc     return -1;
400b8e59f18Smalc }
401b8e59f18Smalc 
402b8e59f18Smalc static void qpa_fini_out (HWVoiceOut *hw)
403b8e59f18Smalc {
404b8e59f18Smalc     void *ret;
405b8e59f18Smalc     PAVoiceOut *pa = (PAVoiceOut *) hw;
406b8e59f18Smalc 
407b8e59f18Smalc     audio_pt_lock (&pa->pt, AUDIO_FUNC);
408b8e59f18Smalc     pa->done = 1;
409b8e59f18Smalc     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
410b8e59f18Smalc     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
411b8e59f18Smalc 
412b8e59f18Smalc     if (pa->s) {
413b8e59f18Smalc         pa_simple_free (pa->s);
414b8e59f18Smalc         pa->s = NULL;
415b8e59f18Smalc     }
416b8e59f18Smalc 
417b8e59f18Smalc     audio_pt_fini (&pa->pt, AUDIO_FUNC);
418b8e59f18Smalc     qemu_free (pa->pcm_buf);
419b8e59f18Smalc     pa->pcm_buf = NULL;
420b8e59f18Smalc }
421b8e59f18Smalc 
422b8e59f18Smalc static void qpa_fini_in (HWVoiceIn *hw)
423b8e59f18Smalc {
424b8e59f18Smalc     void *ret;
425b8e59f18Smalc     PAVoiceIn *pa = (PAVoiceIn *) hw;
426b8e59f18Smalc 
427b8e59f18Smalc     audio_pt_lock (&pa->pt, AUDIO_FUNC);
428b8e59f18Smalc     pa->done = 1;
429b8e59f18Smalc     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
430b8e59f18Smalc     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
431b8e59f18Smalc 
432b8e59f18Smalc     if (pa->s) {
433b8e59f18Smalc         pa_simple_free (pa->s);
434b8e59f18Smalc         pa->s = NULL;
435b8e59f18Smalc     }
436b8e59f18Smalc 
437b8e59f18Smalc     audio_pt_fini (&pa->pt, AUDIO_FUNC);
438b8e59f18Smalc     qemu_free (pa->pcm_buf);
439b8e59f18Smalc     pa->pcm_buf = NULL;
440b8e59f18Smalc }
441b8e59f18Smalc 
442b8e59f18Smalc static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
443b8e59f18Smalc {
444b8e59f18Smalc     (void) hw;
445b8e59f18Smalc     (void) cmd;
446b8e59f18Smalc     return 0;
447b8e59f18Smalc }
448b8e59f18Smalc 
449b8e59f18Smalc static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
450b8e59f18Smalc {
451b8e59f18Smalc     (void) hw;
452b8e59f18Smalc     (void) cmd;
453b8e59f18Smalc     return 0;
454b8e59f18Smalc }
455b8e59f18Smalc 
456b8e59f18Smalc /* common */
457b8e59f18Smalc static void *qpa_audio_init (void)
458b8e59f18Smalc {
459b8e59f18Smalc     return &conf;
460b8e59f18Smalc }
461b8e59f18Smalc 
462b8e59f18Smalc static void qpa_audio_fini (void *opaque)
463b8e59f18Smalc {
464b8e59f18Smalc     (void) opaque;
465b8e59f18Smalc }
466b8e59f18Smalc 
467b8e59f18Smalc struct audio_option qpa_options[] = {
46898f9f48cSmalc     {
46998f9f48cSmalc         .name  = "SAMPLES",
4702700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
4712700efa3SJuan Quintela         .valp  = &conf.samples,
47298f9f48cSmalc         .descr = "buffer size in samples"
47398f9f48cSmalc     },
47498f9f48cSmalc     {
47598f9f48cSmalc         .name  = "DIVISOR",
4762700efa3SJuan Quintela         .tag   = AUD_OPT_INT,
4772700efa3SJuan Quintela         .valp  = &conf.divisor,
47898f9f48cSmalc         .descr = "threshold divisor"
47998f9f48cSmalc     },
48098f9f48cSmalc     {
48198f9f48cSmalc         .name  = "SERVER",
4822700efa3SJuan Quintela         .tag   = AUD_OPT_STR,
4832700efa3SJuan Quintela         .valp  = &conf.server,
48498f9f48cSmalc         .descr = "server address"
48598f9f48cSmalc     },
48698f9f48cSmalc     {
48798f9f48cSmalc         .name  = "SINK",
4882700efa3SJuan Quintela         .tag   = AUD_OPT_STR,
4892700efa3SJuan Quintela         .valp  = &conf.sink,
49098f9f48cSmalc         .descr = "sink device name"
49198f9f48cSmalc     },
49298f9f48cSmalc     {
49398f9f48cSmalc         .name  = "SOURCE",
4942700efa3SJuan Quintela         .tag   = AUD_OPT_STR,
4952700efa3SJuan Quintela         .valp  = &conf.source,
49698f9f48cSmalc         .descr = "source device name"
49798f9f48cSmalc     },
4982700efa3SJuan Quintela     { /* End of list */ }
499b8e59f18Smalc };
500b8e59f18Smalc 
50135f4b58cSblueswir1 static struct audio_pcm_ops qpa_pcm_ops = {
5021dd3e4d1SJuan Quintela     .init_out = qpa_init_out,
5031dd3e4d1SJuan Quintela     .fini_out = qpa_fini_out,
5041dd3e4d1SJuan Quintela     .run_out  = qpa_run_out,
5051dd3e4d1SJuan Quintela     .write    = qpa_write,
5061dd3e4d1SJuan Quintela     .ctl_out  = qpa_ctl_out,
5071dd3e4d1SJuan Quintela 
5081dd3e4d1SJuan Quintela     .init_in  = qpa_init_in,
5091dd3e4d1SJuan Quintela     .fini_in  = qpa_fini_in,
5101dd3e4d1SJuan Quintela     .run_in   = qpa_run_in,
5111dd3e4d1SJuan Quintela     .read     = qpa_read,
5121dd3e4d1SJuan Quintela     .ctl_in   = qpa_ctl_in
513b8e59f18Smalc };
514b8e59f18Smalc 
515b8e59f18Smalc struct audio_driver pa_audio_driver = {
516bee37f32SJuan Quintela     .name           = "pa",
517bee37f32SJuan Quintela     .descr          = "http://www.pulseaudio.org/",
518bee37f32SJuan Quintela     .options        = qpa_options,
519bee37f32SJuan Quintela     .init           = qpa_audio_init,
520bee37f32SJuan Quintela     .fini           = qpa_audio_fini,
521bee37f32SJuan Quintela     .pcm_ops        = &qpa_pcm_ops,
5221a4ea1e3SMichael S. Tsirkin     .can_be_default = 1,
523bee37f32SJuan Quintela     .max_voices_out = INT_MAX,
524bee37f32SJuan Quintela     .max_voices_in  = INT_MAX,
525bee37f32SJuan Quintela     .voice_size_out = sizeof (PAVoiceOut),
526bee37f32SJuan Quintela     .voice_size_in  = sizeof (PAVoiceIn)
527b8e59f18Smalc };
528