]>
Commit | Line | Data |
---|---|---|
1d14ffa9 FB |
1 | /* |
2 | * QEMU ALSA audio driver | |
3 | * | |
4 | * Copyright (c) 2005 Vassili Karpov (malc) | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
6086a565 | 24 | #include "qemu/osdep.h" |
1d14ffa9 | 25 | #include <alsa/asoundlib.h> |
749bc4bf | 26 | #include "qemu-common.h" |
1de7afc9 | 27 | #include "qemu/main-loop.h" |
749bc4bf | 28 | #include "audio.h" |
fbb7ef56 | 29 | #include "trace.h" |
1d14ffa9 | 30 | |
2637872b | 31 | #pragma GCC diagnostic ignored "-Waddress" |
2637872b | 32 | |
1d14ffa9 FB |
33 | #define AUDIO_CAP "alsa" |
34 | #include "audio_int.h" | |
35 | ||
8b438ba3 | 36 | struct pollhlp { |
37 | snd_pcm_t *handle; | |
38 | struct pollfd *pfds; | |
39 | int count; | |
b4f763b8 | 40 | int mask; |
8b438ba3 | 41 | }; |
42 | ||
1d14ffa9 FB |
43 | typedef struct ALSAVoiceOut { |
44 | HWVoiceOut hw; | |
541ba4e7 | 45 | int wpos; |
46 | int pending; | |
1d14ffa9 FB |
47 | void *pcm_buf; |
48 | snd_pcm_t *handle; | |
8b438ba3 | 49 | struct pollhlp pollhlp; |
a93f3281 | 50 | Audiodev *dev; |
1d14ffa9 FB |
51 | } ALSAVoiceOut; |
52 | ||
53 | typedef struct ALSAVoiceIn { | |
54 | HWVoiceIn hw; | |
55 | snd_pcm_t *handle; | |
56 | void *pcm_buf; | |
8b438ba3 | 57 | struct pollhlp pollhlp; |
a93f3281 | 58 | Audiodev *dev; |
1d14ffa9 FB |
59 | } ALSAVoiceIn; |
60 | ||
1d14ffa9 | 61 | struct alsa_params_req { |
ca9cc28c AZ |
62 | int freq; |
63 | snd_pcm_format_t fmt; | |
64 | int nchannels; | |
1d14ffa9 FB |
65 | }; |
66 | ||
67 | struct alsa_params_obt { | |
68 | int freq; | |
85bc5852 | 69 | AudioFormat fmt; |
ca9cc28c | 70 | int endianness; |
1d14ffa9 | 71 | int nchannels; |
c0fe3827 | 72 | snd_pcm_uframes_t samples; |
1d14ffa9 FB |
73 | }; |
74 | ||
75 | static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) | |
76 | { | |
77 | va_list ap; | |
78 | ||
79 | va_start (ap, fmt); | |
80 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
81 | va_end (ap); | |
82 | ||
83 | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
84 | } | |
85 | ||
86 | static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( | |
87 | int err, | |
88 | const char *typ, | |
89 | const char *fmt, | |
90 | ... | |
91 | ) | |
92 | { | |
93 | va_list ap; | |
94 | ||
c0fe3827 | 95 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
1d14ffa9 FB |
96 | |
97 | va_start (ap, fmt); | |
98 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
99 | va_end (ap); | |
100 | ||
101 | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
102 | } | |
103 | ||
6ebfda13 | 104 | static void alsa_fini_poll (struct pollhlp *hlp) |
105 | { | |
106 | int i; | |
107 | struct pollfd *pfds = hlp->pfds; | |
108 | ||
109 | if (pfds) { | |
110 | for (i = 0; i < hlp->count; ++i) { | |
111 | qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); | |
112 | } | |
7267c094 | 113 | g_free (pfds); |
6ebfda13 | 114 | } |
115 | hlp->pfds = NULL; | |
116 | hlp->count = 0; | |
117 | hlp->handle = NULL; | |
118 | } | |
119 | ||
120 | static void alsa_anal_close1 (snd_pcm_t **handlep) | |
1d14ffa9 FB |
121 | { |
122 | int err = snd_pcm_close (*handlep); | |
123 | if (err) { | |
124 | alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); | |
125 | } | |
126 | *handlep = NULL; | |
127 | } | |
128 | ||
6ebfda13 | 129 | static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp) |
130 | { | |
131 | alsa_fini_poll (hlp); | |
132 | alsa_anal_close1 (handlep); | |
133 | } | |
134 | ||
8b438ba3 | 135 | static int alsa_recover (snd_pcm_t *handle) |
136 | { | |
137 | int err = snd_pcm_prepare (handle); | |
138 | if (err < 0) { | |
139 | alsa_logerr (err, "Failed to prepare handle %p\n", handle); | |
140 | return -1; | |
141 | } | |
142 | return 0; | |
143 | } | |
144 | ||
145 | static int alsa_resume (snd_pcm_t *handle) | |
146 | { | |
147 | int err = snd_pcm_resume (handle); | |
148 | if (err < 0) { | |
149 | alsa_logerr (err, "Failed to resume handle %p\n", handle); | |
150 | return -1; | |
151 | } | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static void alsa_poll_handler (void *opaque) | |
156 | { | |
157 | int err, count; | |
158 | snd_pcm_state_t state; | |
159 | struct pollhlp *hlp = opaque; | |
160 | unsigned short revents; | |
161 | ||
162 | count = poll (hlp->pfds, hlp->count, 0); | |
163 | if (count < 0) { | |
164 | dolog ("alsa_poll_handler: poll %s\n", strerror (errno)); | |
165 | return; | |
166 | } | |
167 | ||
168 | if (!count) { | |
169 | return; | |
170 | } | |
171 | ||
172 | /* XXX: ALSA example uses initial count, not the one returned by | |
173 | poll, correct? */ | |
174 | err = snd_pcm_poll_descriptors_revents (hlp->handle, hlp->pfds, | |
175 | hlp->count, &revents); | |
176 | if (err < 0) { | |
177 | alsa_logerr (err, "snd_pcm_poll_descriptors_revents"); | |
178 | return; | |
179 | } | |
180 | ||
b4f763b8 | 181 | if (!(revents & hlp->mask)) { |
fbb7ef56 | 182 | trace_alsa_revents(revents); |
8b438ba3 | 183 | return; |
184 | } | |
185 | ||
186 | state = snd_pcm_state (hlp->handle); | |
187 | switch (state) { | |
d9812b03 | 188 | case SND_PCM_STATE_SETUP: |
189 | alsa_recover (hlp->handle); | |
190 | break; | |
191 | ||
8b438ba3 | 192 | case SND_PCM_STATE_XRUN: |
193 | alsa_recover (hlp->handle); | |
194 | break; | |
195 | ||
196 | case SND_PCM_STATE_SUSPENDED: | |
197 | alsa_resume (hlp->handle); | |
198 | break; | |
199 | ||
200 | case SND_PCM_STATE_PREPARED: | |
201 | audio_run ("alsa run (prepared)"); | |
202 | break; | |
203 | ||
204 | case SND_PCM_STATE_RUNNING: | |
205 | audio_run ("alsa run (running)"); | |
206 | break; | |
207 | ||
208 | default: | |
209 | dolog ("Unexpected state %d\n", state); | |
210 | } | |
211 | } | |
212 | ||
b4f763b8 | 213 | static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) |
8b438ba3 | 214 | { |
215 | int i, count, err; | |
216 | struct pollfd *pfds; | |
217 | ||
218 | count = snd_pcm_poll_descriptors_count (handle); | |
219 | if (count <= 0) { | |
220 | dolog ("Could not initialize poll mode\n" | |
221 | "Invalid number of poll descriptors %d\n", count); | |
222 | return -1; | |
223 | } | |
224 | ||
225 | pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds)); | |
226 | if (!pfds) { | |
227 | dolog ("Could not initialize poll mode\n"); | |
228 | return -1; | |
229 | } | |
230 | ||
231 | err = snd_pcm_poll_descriptors (handle, pfds, count); | |
232 | if (err < 0) { | |
233 | alsa_logerr (err, "Could not initialize poll mode\n" | |
234 | "Could not obtain poll descriptors\n"); | |
7267c094 | 235 | g_free (pfds); |
8b438ba3 | 236 | return -1; |
237 | } | |
238 | ||
239 | for (i = 0; i < count; ++i) { | |
240 | if (pfds[i].events & POLLIN) { | |
be93f216 | 241 | qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp); |
8b438ba3 | 242 | } |
243 | if (pfds[i].events & POLLOUT) { | |
fbb7ef56 | 244 | trace_alsa_pollout(i, pfds[i].fd); |
be93f216 | 245 | qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp); |
8b438ba3 | 246 | } |
fbb7ef56 | 247 | trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err); |
8b438ba3 | 248 | |
8b438ba3 | 249 | } |
250 | hlp->pfds = pfds; | |
251 | hlp->count = count; | |
252 | hlp->handle = handle; | |
b4f763b8 | 253 | hlp->mask = mask; |
8b438ba3 | 254 | return 0; |
255 | } | |
256 | ||
257 | static int alsa_poll_out (HWVoiceOut *hw) | |
258 | { | |
259 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
260 | ||
b4f763b8 | 261 | return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT); |
8b438ba3 | 262 | } |
263 | ||
264 | static int alsa_poll_in (HWVoiceIn *hw) | |
265 | { | |
266 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
267 | ||
b4f763b8 | 268 | return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN); |
8b438ba3 | 269 | } |
270 | ||
1d14ffa9 FB |
271 | static int alsa_write (SWVoiceOut *sw, void *buf, int len) |
272 | { | |
273 | return audio_pcm_sw_write (sw, buf, len); | |
274 | } | |
275 | ||
85bc5852 | 276 | static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) |
1d14ffa9 FB |
277 | { |
278 | switch (fmt) { | |
85bc5852 | 279 | case AUDIO_FORMAT_S8: |
1d14ffa9 FB |
280 | return SND_PCM_FORMAT_S8; |
281 | ||
85bc5852 | 282 | case AUDIO_FORMAT_U8: |
1d14ffa9 FB |
283 | return SND_PCM_FORMAT_U8; |
284 | ||
85bc5852 | 285 | case AUDIO_FORMAT_S16: |
d66bddd7 MW |
286 | if (endianness) { |
287 | return SND_PCM_FORMAT_S16_BE; | |
288 | } | |
289 | else { | |
290 | return SND_PCM_FORMAT_S16_LE; | |
291 | } | |
1d14ffa9 | 292 | |
85bc5852 | 293 | case AUDIO_FORMAT_U16: |
d66bddd7 MW |
294 | if (endianness) { |
295 | return SND_PCM_FORMAT_U16_BE; | |
296 | } | |
297 | else { | |
298 | return SND_PCM_FORMAT_U16_LE; | |
299 | } | |
1d14ffa9 | 300 | |
85bc5852 | 301 | case AUDIO_FORMAT_S32: |
d66bddd7 MW |
302 | if (endianness) { |
303 | return SND_PCM_FORMAT_S32_BE; | |
304 | } | |
305 | else { | |
306 | return SND_PCM_FORMAT_S32_LE; | |
307 | } | |
f941aa25 | 308 | |
85bc5852 | 309 | case AUDIO_FORMAT_U32: |
d66bddd7 MW |
310 | if (endianness) { |
311 | return SND_PCM_FORMAT_U32_BE; | |
312 | } | |
313 | else { | |
314 | return SND_PCM_FORMAT_U32_LE; | |
315 | } | |
f941aa25 | 316 | |
1d14ffa9 FB |
317 | default: |
318 | dolog ("Internal logic error: Bad audio format %d\n", fmt); | |
319 | #ifdef DEBUG_AUDIO | |
320 | abort (); | |
321 | #endif | |
322 | return SND_PCM_FORMAT_U8; | |
323 | } | |
324 | } | |
325 | ||
85bc5852 | 326 | static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, |
ca9cc28c | 327 | int *endianness) |
1d14ffa9 FB |
328 | { |
329 | switch (alsafmt) { | |
330 | case SND_PCM_FORMAT_S8: | |
331 | *endianness = 0; | |
85bc5852 | 332 | *fmt = AUDIO_FORMAT_S8; |
1d14ffa9 FB |
333 | break; |
334 | ||
335 | case SND_PCM_FORMAT_U8: | |
336 | *endianness = 0; | |
85bc5852 | 337 | *fmt = AUDIO_FORMAT_U8; |
1d14ffa9 FB |
338 | break; |
339 | ||
340 | case SND_PCM_FORMAT_S16_LE: | |
341 | *endianness = 0; | |
85bc5852 | 342 | *fmt = AUDIO_FORMAT_S16; |
1d14ffa9 FB |
343 | break; |
344 | ||
345 | case SND_PCM_FORMAT_U16_LE: | |
346 | *endianness = 0; | |
85bc5852 | 347 | *fmt = AUDIO_FORMAT_U16; |
1d14ffa9 FB |
348 | break; |
349 | ||
350 | case SND_PCM_FORMAT_S16_BE: | |
351 | *endianness = 1; | |
85bc5852 | 352 | *fmt = AUDIO_FORMAT_S16; |
1d14ffa9 FB |
353 | break; |
354 | ||
355 | case SND_PCM_FORMAT_U16_BE: | |
356 | *endianness = 1; | |
85bc5852 | 357 | *fmt = AUDIO_FORMAT_U16; |
1d14ffa9 FB |
358 | break; |
359 | ||
f941aa25 TS |
360 | case SND_PCM_FORMAT_S32_LE: |
361 | *endianness = 0; | |
85bc5852 | 362 | *fmt = AUDIO_FORMAT_S32; |
f941aa25 TS |
363 | break; |
364 | ||
365 | case SND_PCM_FORMAT_U32_LE: | |
366 | *endianness = 0; | |
85bc5852 | 367 | *fmt = AUDIO_FORMAT_U32; |
f941aa25 TS |
368 | break; |
369 | ||
370 | case SND_PCM_FORMAT_S32_BE: | |
371 | *endianness = 1; | |
85bc5852 | 372 | *fmt = AUDIO_FORMAT_S32; |
f941aa25 TS |
373 | break; |
374 | ||
375 | case SND_PCM_FORMAT_U32_BE: | |
376 | *endianness = 1; | |
85bc5852 | 377 | *fmt = AUDIO_FORMAT_U32; |
f941aa25 TS |
378 | break; |
379 | ||
1d14ffa9 FB |
380 | default: |
381 | dolog ("Unrecognized audio format %d\n", alsafmt); | |
382 | return -1; | |
383 | } | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
1d14ffa9 | 388 | static void alsa_dump_info (struct alsa_params_req *req, |
8bb414d2 | 389 | struct alsa_params_obt *obt, |
a93f3281 KZ |
390 | snd_pcm_format_t obtfmt, |
391 | AudiodevAlsaPerDirectionOptions *apdo) | |
1d14ffa9 | 392 | { |
a93f3281 KZ |
393 | dolog("parameter | requested value | obtained value\n"); |
394 | dolog("format | %10d | %10d\n", req->fmt, obtfmt); | |
395 | dolog("channels | %10d | %10d\n", | |
396 | req->nchannels, obt->nchannels); | |
397 | dolog("frequency | %10d | %10d\n", req->freq, obt->freq); | |
398 | dolog("============================================\n"); | |
399 | dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", | |
400 | apdo->buffer_length, apdo->period_length); | |
401 | dolog("obtained: samples %ld\n", obt->samples); | |
1d14ffa9 | 402 | } |
1d14ffa9 FB |
403 | |
404 | static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | |
405 | { | |
406 | int err; | |
407 | snd_pcm_sw_params_t *sw_params; | |
408 | ||
409 | snd_pcm_sw_params_alloca (&sw_params); | |
410 | ||
411 | err = snd_pcm_sw_params_current (handle, sw_params); | |
412 | if (err < 0) { | |
c0fe3827 | 413 | dolog ("Could not fully initialize DAC\n"); |
1d14ffa9 FB |
414 | alsa_logerr (err, "Failed to get current software parameters\n"); |
415 | return; | |
416 | } | |
417 | ||
418 | err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); | |
419 | if (err < 0) { | |
c0fe3827 | 420 | dolog ("Could not fully initialize DAC\n"); |
1d14ffa9 FB |
421 | alsa_logerr (err, "Failed to set software threshold to %ld\n", |
422 | threshold); | |
423 | return; | |
424 | } | |
425 | ||
426 | err = snd_pcm_sw_params (handle, sw_params); | |
427 | if (err < 0) { | |
c0fe3827 | 428 | dolog ("Could not fully initialize DAC\n"); |
1d14ffa9 FB |
429 | alsa_logerr (err, "Failed to set software parameters\n"); |
430 | return; | |
431 | } | |
432 | } | |
433 | ||
a93f3281 KZ |
434 | static int alsa_open(bool in, struct alsa_params_req *req, |
435 | struct alsa_params_obt *obt, snd_pcm_t **handlep, | |
436 | Audiodev *dev) | |
1d14ffa9 | 437 | { |
a93f3281 KZ |
438 | AudiodevAlsaOptions *aopts = &dev->u.alsa; |
439 | AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out; | |
1d14ffa9 FB |
440 | snd_pcm_t *handle; |
441 | snd_pcm_hw_params_t *hw_params; | |
60fe76f3 TS |
442 | int err; |
443 | unsigned int freq, nchannels; | |
a93f3281 | 444 | const char *pcm_name = apdo->has_dev ? apdo->dev : "default"; |
1d14ffa9 FB |
445 | snd_pcm_uframes_t obt_buffer_size; |
446 | const char *typ = in ? "ADC" : "DAC"; | |
ca9cc28c | 447 | snd_pcm_format_t obtfmt; |
1d14ffa9 FB |
448 | |
449 | freq = req->freq; | |
1d14ffa9 FB |
450 | nchannels = req->nchannels; |
451 | ||
452 | snd_pcm_hw_params_alloca (&hw_params); | |
453 | ||
454 | err = snd_pcm_open ( | |
455 | &handle, | |
456 | pcm_name, | |
457 | in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | |
458 | SND_PCM_NONBLOCK | |
459 | ); | |
460 | if (err < 0) { | |
461 | alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); | |
462 | return -1; | |
463 | } | |
464 | ||
465 | err = snd_pcm_hw_params_any (handle, hw_params); | |
466 | if (err < 0) { | |
467 | alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); | |
468 | goto err; | |
469 | } | |
470 | ||
471 | err = snd_pcm_hw_params_set_access ( | |
472 | handle, | |
473 | hw_params, | |
474 | SND_PCM_ACCESS_RW_INTERLEAVED | |
475 | ); | |
476 | if (err < 0) { | |
477 | alsa_logerr2 (err, typ, "Failed to set access type\n"); | |
478 | goto err; | |
479 | } | |
480 | ||
481 | err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); | |
fbb7ef56 | 482 | if (err < 0) { |
1d14ffa9 | 483 | alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); |
1d14ffa9 FB |
484 | } |
485 | ||
486 | err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); | |
487 | if (err < 0) { | |
488 | alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); | |
489 | goto err; | |
490 | } | |
491 | ||
492 | err = snd_pcm_hw_params_set_channels_near ( | |
493 | handle, | |
494 | hw_params, | |
495 | &nchannels | |
496 | ); | |
497 | if (err < 0) { | |
498 | alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", | |
499 | req->nchannels); | |
500 | goto err; | |
501 | } | |
502 | ||
503 | if (nchannels != 1 && nchannels != 2) { | |
504 | alsa_logerr2 (err, typ, | |
505 | "Can not handle obtained number of channels %d\n", | |
506 | nchannels); | |
507 | goto err; | |
508 | } | |
509 | ||
a93f3281 KZ |
510 | if (apdo->buffer_length) { |
511 | int dir = 0; | |
512 | unsigned int btime = apdo->buffer_length; | |
f3b52983 | 513 | |
a93f3281 KZ |
514 | err = snd_pcm_hw_params_set_buffer_time_near( |
515 | handle, hw_params, &btime, &dir); | |
1d14ffa9 | 516 | |
7a24c800 | 517 | if (err < 0) { |
a93f3281 KZ |
518 | alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", |
519 | apdo->buffer_length); | |
7a24c800 | 520 | goto err; |
521 | } | |
f3b52983 | 522 | |
a93f3281 KZ |
523 | if (apdo->has_buffer_length && btime != apdo->buffer_length) { |
524 | dolog("Requested buffer time %" PRId32 | |
525 | " was rejected, using %u\n", apdo->buffer_length, btime); | |
526 | } | |
7a24c800 | 527 | } |
528 | ||
a93f3281 KZ |
529 | if (apdo->period_length) { |
530 | int dir = 0; | |
531 | unsigned int ptime = apdo->period_length; | |
f3b52983 | 532 | |
a93f3281 KZ |
533 | err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime, |
534 | &dir); | |
7a24c800 | 535 | |
536 | if (err < 0) { | |
a93f3281 KZ |
537 | alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n", |
538 | apdo->period_length); | |
7a24c800 | 539 | goto err; |
540 | } | |
f3b52983 | 541 | |
a93f3281 KZ |
542 | if (apdo->has_period_length && ptime != apdo->period_length) { |
543 | dolog("Requested period time %" PRId32 " was rejected, using %d\n", | |
544 | apdo->period_length, ptime); | |
545 | } | |
1d14ffa9 FB |
546 | } |
547 | ||
548 | err = snd_pcm_hw_params (handle, hw_params); | |
549 | if (err < 0) { | |
550 | alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); | |
551 | goto err; | |
552 | } | |
553 | ||
554 | err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); | |
555 | if (err < 0) { | |
556 | alsa_logerr2 (err, typ, "Failed to get buffer size\n"); | |
557 | goto err; | |
558 | } | |
559 | ||
ca9cc28c AZ |
560 | err = snd_pcm_hw_params_get_format (hw_params, &obtfmt); |
561 | if (err < 0) { | |
562 | alsa_logerr2 (err, typ, "Failed to get format\n"); | |
563 | goto err; | |
564 | } | |
565 | ||
566 | if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) { | |
567 | dolog ("Invalid format was returned %d\n", obtfmt); | |
568 | goto err; | |
569 | } | |
570 | ||
1d14ffa9 FB |
571 | err = snd_pcm_prepare (handle); |
572 | if (err < 0) { | |
c0fe3827 | 573 | alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle); |
1d14ffa9 FB |
574 | goto err; |
575 | } | |
576 | ||
a93f3281 KZ |
577 | if (!in && aopts->has_threshold && aopts->threshold) { |
578 | struct audsettings as = { .freq = freq }; | |
579 | alsa_set_threshold( | |
580 | handle, | |
581 | audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), | |
582 | &as, aopts->threshold)); | |
1d14ffa9 FB |
583 | } |
584 | ||
1d14ffa9 FB |
585 | obt->nchannels = nchannels; |
586 | obt->freq = freq; | |
c0fe3827 | 587 | obt->samples = obt_buffer_size; |
ca9cc28c | 588 | |
1d14ffa9 FB |
589 | *handlep = handle; |
590 | ||
fbb7ef56 | 591 | if (obtfmt != req->fmt || |
ca9cc28c | 592 | obt->nchannels != req->nchannels || |
fbb7ef56 | 593 | obt->freq != req->freq) { |
f093feb7 | 594 | dolog ("Audio parameters for %s\n", typ); |
a93f3281 | 595 | alsa_dump_info(req, obt, obtfmt, apdo); |
1d14ffa9 FB |
596 | } |
597 | ||
598 | #ifdef DEBUG | |
a93f3281 | 599 | alsa_dump_info(req, obt, obtfmt, pdo); |
1d14ffa9 FB |
600 | #endif |
601 | return 0; | |
602 | ||
603 | err: | |
6ebfda13 | 604 | alsa_anal_close1 (&handle); |
1d14ffa9 FB |
605 | return -1; |
606 | } | |
607 | ||
571ec3d6 FB |
608 | static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) |
609 | { | |
610 | snd_pcm_sframes_t avail; | |
611 | ||
612 | avail = snd_pcm_avail_update (handle); | |
613 | if (avail < 0) { | |
614 | if (avail == -EPIPE) { | |
615 | if (!alsa_recover (handle)) { | |
616 | avail = snd_pcm_avail_update (handle); | |
617 | } | |
618 | } | |
619 | ||
620 | if (avail < 0) { | |
621 | alsa_logerr (avail, | |
622 | "Could not obtain number of available frames\n"); | |
623 | return -1; | |
624 | } | |
625 | } | |
626 | ||
627 | return avail; | |
628 | } | |
629 | ||
541ba4e7 | 630 | static void alsa_write_pending (ALSAVoiceOut *alsa) |
1d14ffa9 | 631 | { |
541ba4e7 | 632 | HWVoiceOut *hw = &alsa->hw; |
1d14ffa9 | 633 | |
541ba4e7 | 634 | while (alsa->pending) { |
635 | int left_till_end_samples = hw->samples - alsa->wpos; | |
636 | int len = audio_MIN (alsa->pending, left_till_end_samples); | |
637 | char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); | |
1d14ffa9 | 638 | |
571ec3d6 | 639 | while (len) { |
541ba4e7 | 640 | snd_pcm_sframes_t written; |
641 | ||
642 | written = snd_pcm_writei (alsa->handle, src, len); | |
4787c71d | 643 | |
571ec3d6 | 644 | if (written <= 0) { |
4787c71d | 645 | switch (written) { |
571ec3d6 | 646 | case 0: |
fbb7ef56 | 647 | trace_alsa_wrote_zero(len); |
541ba4e7 | 648 | return; |
4787c71d | 649 | |
571ec3d6 FB |
650 | case -EPIPE: |
651 | if (alsa_recover (alsa->handle)) { | |
652 | alsa_logerr (written, "Failed to write %d frames\n", | |
653 | len); | |
541ba4e7 | 654 | return; |
571ec3d6 | 655 | } |
fbb7ef56 | 656 | trace_alsa_xrun_out(); |
4787c71d FB |
657 | continue; |
658 | ||
86635821 BM |
659 | case -ESTRPIPE: |
660 | /* stream is suspended and waiting for an | |
661 | application recovery */ | |
662 | if (alsa_resume (alsa->handle)) { | |
663 | alsa_logerr (written, "Failed to write %d frames\n", | |
664 | len); | |
541ba4e7 | 665 | return; |
86635821 | 666 | } |
fbb7ef56 | 667 | trace_alsa_resume_out(); |
86635821 BM |
668 | continue; |
669 | ||
571ec3d6 | 670 | case -EAGAIN: |
541ba4e7 | 671 | return; |
571ec3d6 | 672 | |
4787c71d | 673 | default: |
541ba4e7 | 674 | alsa_logerr (written, "Failed to write %d frames from %p\n", |
675 | len, src); | |
676 | return; | |
1d14ffa9 | 677 | } |
1d14ffa9 | 678 | } |
1d14ffa9 | 679 | |
541ba4e7 | 680 | alsa->wpos = (alsa->wpos + written) % hw->samples; |
681 | alsa->pending -= written; | |
571ec3d6 | 682 | len -= written; |
4787c71d | 683 | } |
1d14ffa9 | 684 | } |
541ba4e7 | 685 | } |
686 | ||
bdff253c | 687 | static int alsa_run_out (HWVoiceOut *hw, int live) |
541ba4e7 | 688 | { |
689 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
bdff253c | 690 | int decr; |
541ba4e7 | 691 | snd_pcm_sframes_t avail; |
692 | ||
541ba4e7 | 693 | avail = alsa_get_avail (alsa->handle); |
694 | if (avail < 0) { | |
695 | dolog ("Could not get number of available playback frames\n"); | |
696 | return 0; | |
697 | } | |
698 | ||
699 | decr = audio_MIN (live, avail); | |
700 | decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); | |
701 | alsa->pending += decr; | |
702 | alsa_write_pending (alsa); | |
1d14ffa9 FB |
703 | return decr; |
704 | } | |
705 | ||
706 | static void alsa_fini_out (HWVoiceOut *hw) | |
707 | { | |
708 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
709 | ||
710 | ldebug ("alsa_fini\n"); | |
6ebfda13 | 711 | alsa_anal_close (&alsa->handle, &alsa->pollhlp); |
1d14ffa9 | 712 | |
fb7da626 MA |
713 | g_free(alsa->pcm_buf); |
714 | alsa->pcm_buf = NULL; | |
1d14ffa9 FB |
715 | } |
716 | ||
5706db1d KZ |
717 | static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, |
718 | void *drv_opaque) | |
1d14ffa9 FB |
719 | { |
720 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
721 | struct alsa_params_req req; | |
722 | struct alsa_params_obt obt; | |
1d14ffa9 | 723 | snd_pcm_t *handle; |
1ea879e5 | 724 | struct audsettings obt_as; |
a93f3281 | 725 | Audiodev *dev = drv_opaque; |
1d14ffa9 | 726 | |
d66bddd7 | 727 | req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |
c0fe3827 FB |
728 | req.freq = as->freq; |
729 | req.nchannels = as->nchannels; | |
a93f3281 KZ |
730 | |
731 | if (alsa_open(0, &req, &obt, &handle, dev)) { | |
1d14ffa9 FB |
732 | return -1; |
733 | } | |
734 | ||
c0fe3827 FB |
735 | obt_as.freq = obt.freq; |
736 | obt_as.nchannels = obt.nchannels; | |
ca9cc28c AZ |
737 | obt_as.fmt = obt.fmt; |
738 | obt_as.endianness = obt.endianness; | |
c0fe3827 | 739 | |
d929eba5 | 740 | audio_pcm_init_info (&hw->info, &obt_as); |
c0fe3827 | 741 | hw->samples = obt.samples; |
1d14ffa9 | 742 | |
470bcabd | 743 | alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift); |
1d14ffa9 | 744 | if (!alsa->pcm_buf) { |
4787c71d FB |
745 | dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", |
746 | hw->samples, 1 << hw->info.shift); | |
6ebfda13 | 747 | alsa_anal_close1 (&handle); |
1d14ffa9 FB |
748 | return -1; |
749 | } | |
750 | ||
751 | alsa->handle = handle; | |
a93f3281 | 752 | alsa->dev = dev; |
1d14ffa9 FB |
753 | return 0; |
754 | } | |
755 | ||
38cc9b60 JM |
756 | #define VOICE_CTL_PAUSE 0 |
757 | #define VOICE_CTL_PREPARE 1 | |
758 | #define VOICE_CTL_START 2 | |
759 | ||
760 | static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) | |
1d14ffa9 FB |
761 | { |
762 | int err; | |
571ec3d6 | 763 | |
38cc9b60 | 764 | if (ctl == VOICE_CTL_PAUSE) { |
571ec3d6 FB |
765 | err = snd_pcm_drop (handle); |
766 | if (err < 0) { | |
32d448c4 | 767 | alsa_logerr (err, "Could not stop %s\n", typ); |
571ec3d6 FB |
768 | return -1; |
769 | } | |
770 | } | |
771 | else { | |
772 | err = snd_pcm_prepare (handle); | |
773 | if (err < 0) { | |
32d448c4 | 774 | alsa_logerr (err, "Could not prepare handle for %s\n", typ); |
571ec3d6 FB |
775 | return -1; |
776 | } | |
38cc9b60 JM |
777 | if (ctl == VOICE_CTL_START) { |
778 | err = snd_pcm_start(handle); | |
779 | if (err < 0) { | |
780 | alsa_logerr (err, "Could not start handle for %s\n", typ); | |
781 | return -1; | |
782 | } | |
783 | } | |
571ec3d6 FB |
784 | } |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
789 | static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
790 | { | |
1d14ffa9 | 791 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
a93f3281 | 792 | AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out; |
1d14ffa9 FB |
793 | |
794 | switch (cmd) { | |
795 | case VOICE_ENABLE: | |
301901b5 | 796 | { |
a93f3281 | 797 | bool poll_mode = apdo->try_poll; |
301901b5 | 798 | |
799 | ldebug ("enabling voice\n"); | |
800 | if (poll_mode && alsa_poll_out (hw)) { | |
801 | poll_mode = 0; | |
802 | } | |
803 | hw->poll_mode = poll_mode; | |
38cc9b60 | 804 | return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); |
8b438ba3 | 805 | } |
1d14ffa9 FB |
806 | |
807 | case VOICE_DISABLE: | |
808 | ldebug ("disabling voice\n"); | |
22d948a2 JM |
809 | if (hw->poll_mode) { |
810 | hw->poll_mode = 0; | |
811 | alsa_fini_poll (&alsa->pollhlp); | |
812 | } | |
38cc9b60 | 813 | return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); |
1d14ffa9 | 814 | } |
571ec3d6 FB |
815 | |
816 | return -1; | |
1d14ffa9 FB |
817 | } |
818 | ||
5706db1d | 819 | static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) |
1d14ffa9 FB |
820 | { |
821 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
822 | struct alsa_params_req req; | |
823 | struct alsa_params_obt obt; | |
1d14ffa9 | 824 | snd_pcm_t *handle; |
1ea879e5 | 825 | struct audsettings obt_as; |
a93f3281 | 826 | Audiodev *dev = drv_opaque; |
1d14ffa9 | 827 | |
d66bddd7 | 828 | req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |
c0fe3827 FB |
829 | req.freq = as->freq; |
830 | req.nchannels = as->nchannels; | |
a93f3281 KZ |
831 | |
832 | if (alsa_open(1, &req, &obt, &handle, dev)) { | |
1d14ffa9 FB |
833 | return -1; |
834 | } | |
835 | ||
c0fe3827 FB |
836 | obt_as.freq = obt.freq; |
837 | obt_as.nchannels = obt.nchannels; | |
ca9cc28c AZ |
838 | obt_as.fmt = obt.fmt; |
839 | obt_as.endianness = obt.endianness; | |
c0fe3827 | 840 | |
d929eba5 | 841 | audio_pcm_init_info (&hw->info, &obt_as); |
c0fe3827 FB |
842 | hw->samples = obt.samples; |
843 | ||
470bcabd | 844 | alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |
1d14ffa9 | 845 | if (!alsa->pcm_buf) { |
4787c71d FB |
846 | dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", |
847 | hw->samples, 1 << hw->info.shift); | |
6ebfda13 | 848 | alsa_anal_close1 (&handle); |
1d14ffa9 FB |
849 | return -1; |
850 | } | |
851 | ||
852 | alsa->handle = handle; | |
a93f3281 | 853 | alsa->dev = dev; |
1d14ffa9 FB |
854 | return 0; |
855 | } | |
856 | ||
857 | static void alsa_fini_in (HWVoiceIn *hw) | |
858 | { | |
859 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
860 | ||
6ebfda13 | 861 | alsa_anal_close (&alsa->handle, &alsa->pollhlp); |
1d14ffa9 | 862 | |
fb7da626 MA |
863 | g_free(alsa->pcm_buf); |
864 | alsa->pcm_buf = NULL; | |
1d14ffa9 FB |
865 | } |
866 | ||
867 | static int alsa_run_in (HWVoiceIn *hw) | |
868 | { | |
869 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
870 | int hwshift = hw->info.shift; | |
871 | int i; | |
872 | int live = audio_pcm_hw_get_live_in (hw); | |
873 | int dead = hw->samples - live; | |
571ec3d6 | 874 | int decr; |
1d14ffa9 FB |
875 | struct { |
876 | int add; | |
877 | int len; | |
878 | } bufs[2] = { | |
98f9f48c | 879 | { .add = hw->wpos, .len = 0 }, |
880 | { .add = 0, .len = 0 } | |
1d14ffa9 | 881 | }; |
571ec3d6 | 882 | snd_pcm_sframes_t avail; |
1d14ffa9 FB |
883 | snd_pcm_uframes_t read_samples = 0; |
884 | ||
885 | if (!dead) { | |
886 | return 0; | |
887 | } | |
888 | ||
571ec3d6 FB |
889 | avail = alsa_get_avail (alsa->handle); |
890 | if (avail < 0) { | |
891 | dolog ("Could not get number of captured frames\n"); | |
892 | return 0; | |
893 | } | |
894 | ||
86635821 BM |
895 | if (!avail) { |
896 | snd_pcm_state_t state; | |
897 | ||
898 | state = snd_pcm_state (alsa->handle); | |
899 | switch (state) { | |
900 | case SND_PCM_STATE_PREPARED: | |
901 | avail = hw->samples; | |
902 | break; | |
903 | case SND_PCM_STATE_SUSPENDED: | |
904 | /* stream is suspended and waiting for an application recovery */ | |
905 | if (alsa_resume (alsa->handle)) { | |
906 | dolog ("Failed to resume suspended input stream\n"); | |
907 | return 0; | |
908 | } | |
fbb7ef56 | 909 | trace_alsa_resume_in(); |
86635821 BM |
910 | break; |
911 | default: | |
fbb7ef56 | 912 | trace_alsa_no_frames(state); |
86635821 BM |
913 | return 0; |
914 | } | |
571ec3d6 FB |
915 | } |
916 | ||
917 | decr = audio_MIN (dead, avail); | |
918 | if (!decr) { | |
919 | return 0; | |
920 | } | |
921 | ||
922 | if (hw->wpos + decr > hw->samples) { | |
1d14ffa9 | 923 | bufs[0].len = (hw->samples - hw->wpos); |
571ec3d6 | 924 | bufs[1].len = (decr - (hw->samples - hw->wpos)); |
1d14ffa9 FB |
925 | } |
926 | else { | |
571ec3d6 | 927 | bufs[0].len = decr; |
1d14ffa9 FB |
928 | } |
929 | ||
1d14ffa9 FB |
930 | for (i = 0; i < 2; ++i) { |
931 | void *src; | |
1ea879e5 | 932 | struct st_sample *dst; |
1d14ffa9 FB |
933 | snd_pcm_sframes_t nread; |
934 | snd_pcm_uframes_t len; | |
935 | ||
936 | len = bufs[i].len; | |
937 | ||
938 | src = advance (alsa->pcm_buf, bufs[i].add << hwshift); | |
939 | dst = hw->conv_buf + bufs[i].add; | |
940 | ||
941 | while (len) { | |
942 | nread = snd_pcm_readi (alsa->handle, src, len); | |
943 | ||
571ec3d6 | 944 | if (nread <= 0) { |
1d14ffa9 | 945 | switch (nread) { |
571ec3d6 | 946 | case 0: |
fbb7ef56 | 947 | trace_alsa_read_zero(len); |
1d14ffa9 FB |
948 | goto exit; |
949 | ||
571ec3d6 FB |
950 | case -EPIPE: |
951 | if (alsa_recover (alsa->handle)) { | |
952 | alsa_logerr (nread, "Failed to read %ld frames\n", len); | |
953 | goto exit; | |
954 | } | |
fbb7ef56 | 955 | trace_alsa_xrun_in(); |
1d14ffa9 FB |
956 | continue; |
957 | ||
571ec3d6 FB |
958 | case -EAGAIN: |
959 | goto exit; | |
960 | ||
1d14ffa9 FB |
961 | default: |
962 | alsa_logerr ( | |
963 | nread, | |
964 | "Failed to read %ld frames from %p\n", | |
965 | len, | |
966 | src | |
967 | ); | |
968 | goto exit; | |
969 | } | |
970 | } | |
971 | ||
00e07679 | 972 | hw->conv (dst, src, nread); |
1d14ffa9 FB |
973 | |
974 | src = advance (src, nread << hwshift); | |
975 | dst += nread; | |
976 | ||
977 | read_samples += nread; | |
978 | len -= nread; | |
979 | } | |
980 | } | |
981 | ||
982 | exit: | |
983 | hw->wpos = (hw->wpos + read_samples) % hw->samples; | |
984 | return read_samples; | |
985 | } | |
986 | ||
987 | static int alsa_read (SWVoiceIn *sw, void *buf, int size) | |
988 | { | |
989 | return audio_pcm_sw_read (sw, buf, size); | |
990 | } | |
991 | ||
992 | static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |
993 | { | |
571ec3d6 | 994 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
a93f3281 | 995 | AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in; |
571ec3d6 FB |
996 | |
997 | switch (cmd) { | |
998 | case VOICE_ENABLE: | |
a628b869 | 999 | { |
a93f3281 | 1000 | bool poll_mode = apdo->try_poll; |
a628b869 | 1001 | |
1002 | ldebug ("enabling voice\n"); | |
1003 | if (poll_mode && alsa_poll_in (hw)) { | |
1004 | poll_mode = 0; | |
1005 | } | |
1006 | hw->poll_mode = poll_mode; | |
1007 | ||
38cc9b60 | 1008 | return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); |
a628b869 | 1009 | } |
571ec3d6 FB |
1010 | |
1011 | case VOICE_DISABLE: | |
1012 | ldebug ("disabling voice\n"); | |
8b438ba3 | 1013 | if (hw->poll_mode) { |
1014 | hw->poll_mode = 0; | |
1015 | alsa_fini_poll (&alsa->pollhlp); | |
1016 | } | |
38cc9b60 | 1017 | return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); |
571ec3d6 FB |
1018 | } |
1019 | ||
1020 | return -1; | |
1d14ffa9 FB |
1021 | } |
1022 | ||
a93f3281 KZ |
1023 | static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) |
1024 | { | |
1025 | if (!apdo->has_try_poll) { | |
1026 | apdo->try_poll = true; | |
1027 | apdo->has_try_poll = true; | |
1028 | } | |
1029 | } | |
765b37da | 1030 | |
71830221 | 1031 | static void *alsa_audio_init(Audiodev *dev) |
1d14ffa9 | 1032 | { |
a93f3281 KZ |
1033 | AudiodevAlsaOptions *aopts; |
1034 | assert(dev->driver == AUDIODEV_DRIVER_ALSA); | |
1035 | ||
1036 | aopts = &dev->u.alsa; | |
1037 | alsa_init_per_direction(aopts->in); | |
1038 | alsa_init_per_direction(aopts->out); | |
1039 | ||
1040 | /* | |
1041 | * need to define them, as otherwise alsa produces no sound | |
1042 | * doesn't set has_* so alsa_open can identify it wasn't set by the user | |
1043 | */ | |
1044 | if (!dev->u.alsa.out->has_period_length) { | |
1045 | /* 1024 frames assuming 44100Hz */ | |
1046 | dev->u.alsa.out->period_length = 1024 * 1000000 / 44100; | |
1047 | } | |
1048 | if (!dev->u.alsa.out->has_buffer_length) { | |
1049 | /* 4096 frames assuming 44100Hz */ | |
1050 | dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100; | |
1051 | } | |
1052 | ||
1053 | /* | |
1054 | * OptsVisitor sets unspecified optional fields to zero, but do not depend | |
1055 | * on it... | |
1056 | */ | |
1057 | if (!dev->u.alsa.in->has_period_length) { | |
1058 | dev->u.alsa.in->period_length = 0; | |
1059 | } | |
1060 | if (!dev->u.alsa.in->has_buffer_length) { | |
1061 | dev->u.alsa.in->buffer_length = 0; | |
1062 | } | |
1063 | ||
1064 | return dev; | |
1d14ffa9 FB |
1065 | } |
1066 | ||
1067 | static void alsa_audio_fini (void *opaque) | |
1068 | { | |
1d14ffa9 FB |
1069 | } |
1070 | ||
35f4b58c | 1071 | static struct audio_pcm_ops alsa_pcm_ops = { |
1dd3e4d1 JQ |
1072 | .init_out = alsa_init_out, |
1073 | .fini_out = alsa_fini_out, | |
1074 | .run_out = alsa_run_out, | |
1075 | .write = alsa_write, | |
1076 | .ctl_out = alsa_ctl_out, | |
1077 | ||
1078 | .init_in = alsa_init_in, | |
1079 | .fini_in = alsa_fini_in, | |
1080 | .run_in = alsa_run_in, | |
1081 | .read = alsa_read, | |
8b438ba3 | 1082 | .ctl_in = alsa_ctl_in, |
1d14ffa9 FB |
1083 | }; |
1084 | ||
d3893a39 | 1085 | static struct audio_driver alsa_audio_driver = { |
bee37f32 JQ |
1086 | .name = "alsa", |
1087 | .descr = "ALSA http://www.alsa-project.org", | |
bee37f32 JQ |
1088 | .init = alsa_audio_init, |
1089 | .fini = alsa_audio_fini, | |
1090 | .pcm_ops = &alsa_pcm_ops, | |
1091 | .can_be_default = 1, | |
1092 | .max_voices_out = INT_MAX, | |
1093 | .max_voices_in = INT_MAX, | |
1094 | .voice_size_out = sizeof (ALSAVoiceOut), | |
1095 | .voice_size_in = sizeof (ALSAVoiceIn) | |
1d14ffa9 | 1096 | }; |
d3893a39 GH |
1097 | |
1098 | static void register_audio_alsa(void) | |
1099 | { | |
1100 | audio_driver_register(&alsa_audio_driver); | |
1101 | } | |
1102 | type_init(register_audio_alsa); |