1 /*
2 * Copyright © 2017 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Paul Kocialkowski <paul.kocialkowski@linux.intel.com>
25 */
26
27 #include "config.h"
28
29 #include <limits.h>
30
31 #include "igt_alsa.h"
32 #include "igt_aux.h"
33 #include "igt_core.h"
34
35 #define HANDLES_MAX 8
36
37 /**
38 * SECTION:igt_alsa
39 * @short_description: Library with ALSA helpers
40 * @title: ALSA
41 * @include: igt_alsa.h
42 *
43 * This library contains helpers for ALSA playback and capture.
44 */
45
46 struct alsa {
47 snd_pcm_t *output_handles[HANDLES_MAX];
48 int output_handles_count;
49 snd_pcm_format_t output_format;
50 int output_sampling_rate;
51 int output_channels;
52
53 int (*output_callback)(void *data, void *buffer, int samples);
54 void *output_callback_data;
55 int output_samples_trigger;
56 };
57
58 /**
59 * alsa_has_exclusive_access:
60 * Check whether ALSA has exclusive access to audio devices. Fails if
61 * PulseAudio is running.
62 */
alsa_has_exclusive_access(void)63 bool alsa_has_exclusive_access(void)
64 {
65 if (igt_is_process_running("pulseaudio")) {
66 igt_warn("alsa doesn't have exclusive access to audio devices\n");
67 igt_warn("It seems that PulseAudio is running. Audio tests "
68 "need direct access to audio devices, so PulseAudio "
69 "needs to be stopped. You can do so by running "
70 "`pulseaudio --kill`. Also make sure to add "
71 "autospawn=no to /etc/pulse/client.conf\n");
72 return false;
73 }
74
75 return true;
76 }
77
alsa_error_handler(const char * file,int line,const char * function,int err,const char * fmt,...)78 static void alsa_error_handler(const char *file, int line, const char *function,
79 int err, const char *fmt, ...)
80 {
81 if (err)
82 igt_debug("[ALSA] %s: %s\n", function, snd_strerror(err));
83 }
84
85 /**
86 * alsa_init:
87 * Allocate and initialize an alsa structure and configure the error handler.
88 *
89 * Returns: A newly-allocated alsa structure
90 */
alsa_init(void)91 struct alsa *alsa_init(void)
92 {
93 struct alsa *alsa;
94
95 if (!alsa_has_exclusive_access()) {
96 return NULL;
97 }
98
99 alsa = malloc(sizeof(struct alsa));
100 memset(alsa, 0, sizeof(struct alsa));
101
102 /* Redirect errors to igt_debug instead of stderr. */
103 snd_lib_error_set_handler(alsa_error_handler);
104
105 return alsa;
106 }
107
alsa_resolve_indentifier(const char * device_name,int skip)108 static char *alsa_resolve_indentifier(const char *device_name, int skip)
109 {
110 snd_ctl_card_info_t *card_info;
111 snd_pcm_info_t *pcm_info;
112 snd_ctl_t *handle = NULL;
113 const char *pcm_name;
114 char *identifier = NULL;
115 char name[32];
116 int card = -1;
117 int dev;
118 int ret;
119
120 snd_ctl_card_info_alloca(&card_info);
121 snd_pcm_info_alloca(&pcm_info);
122
123 /* First try to open the device as-is. */
124 if (!skip) {
125 ret = snd_ctl_open(&handle, device_name, 0);
126 if (!ret) {
127 identifier = strdup(device_name);
128 goto resolved;
129 }
130 }
131
132 do {
133 ret = snd_card_next(&card);
134 if (ret < 0 || card < 0)
135 break;
136
137 snprintf(name, sizeof(name), "hw:%d", card);
138
139 ret = snd_ctl_open(&handle, name, 0);
140 if (ret < 0)
141 continue;
142
143 ret = snd_ctl_card_info(handle, card_info);
144 if (ret < 0) {
145 snd_ctl_close(handle);
146 handle = NULL;
147 continue;
148 }
149
150 dev = -1;
151
152 do {
153 ret = snd_ctl_pcm_next_device(handle, &dev);
154 if (ret < 0 || dev < 0)
155 break;
156
157 snd_pcm_info_set_device(pcm_info, dev);
158 snd_pcm_info_set_subdevice(pcm_info, 0);
159
160 ret = snd_ctl_pcm_info(handle, pcm_info);
161 if (ret < 0)
162 continue;
163
164 pcm_name = snd_pcm_info_get_name(pcm_info);
165 if (!pcm_name)
166 continue;
167
168 ret = strncmp(device_name, pcm_name,
169 strlen(device_name));
170
171 if (ret == 0) {
172 if (skip > 0) {
173 skip--;
174 continue;
175 }
176
177 igt_debug("Matched device \"%s\"\n", pcm_name);
178
179 snprintf(name, sizeof(name), "hw:%d,%d", card,
180 dev);
181
182 identifier = strdup(name);
183 goto resolved;
184 }
185 } while (dev >= 0);
186
187 snd_ctl_close(handle);
188 handle = NULL;
189 } while (card >= 0);
190
191 resolved:
192 if (handle)
193 snd_ctl_close(handle);
194
195 return identifier;
196 }
197
198 /**
199 * alsa_open_output:
200 * @alsa: The target alsa structure
201 * @device_name: The name prefix of the output device(s) to open
202 *
203 * Open ALSA output devices whose name prefixes match the provided name prefix.
204 *
205 * Returns: An integer equal to zero for success and negative for failure
206 */
alsa_open_output(struct alsa * alsa,const char * device_name)207 int alsa_open_output(struct alsa *alsa, const char *device_name)
208 {
209 snd_pcm_t *handle;
210 char *identifier;
211 int skip;
212 int index;
213 int ret;
214
215 skip = alsa->output_handles_count;
216 index = alsa->output_handles_count;
217
218 while (index < HANDLES_MAX) {
219 identifier = alsa_resolve_indentifier(device_name, skip++);
220 if (!identifier)
221 break;
222
223 ret = snd_pcm_open(&handle, identifier, SND_PCM_STREAM_PLAYBACK,
224 SND_PCM_NONBLOCK);
225 if (ret < 0) {
226 free(identifier);
227 continue;
228 }
229
230 igt_debug("Opened output %s\n", identifier);
231
232 alsa->output_handles[index++] = handle;
233 free(identifier);
234 }
235
236 if (index == 0)
237 return -1;
238
239 alsa->output_handles_count = index;
240
241 return 0;
242 }
243
244 /**
245 * alsa_close_output:
246 * @alsa: The target alsa structure
247 *
248 * Close all the open ALSA outputs.
249 */
alsa_close_output(struct alsa * alsa)250 void alsa_close_output(struct alsa *alsa)
251 {
252 snd_pcm_t *handle;
253 int i;
254
255 for (i = 0; i < alsa->output_handles_count; i++) {
256 handle = alsa->output_handles[i];
257 if (!handle)
258 continue;
259
260 snd_pcm_close(handle);
261 alsa->output_handles[i] = NULL;
262 }
263
264 alsa->output_handles_count = 0;
265
266 alsa->output_callback = NULL;
267 }
268
alsa_test_configuration(snd_pcm_t * handle,snd_pcm_format_t fmt,int channels,int sampling_rate)269 static bool alsa_test_configuration(snd_pcm_t *handle, snd_pcm_format_t fmt,
270 int channels, int sampling_rate)
271 {
272 snd_pcm_hw_params_t *params;
273 int ret;
274 unsigned int min_channels, max_channels;
275 unsigned int min_rate, max_rate;
276 int min_rate_dir, max_rate_dir;
277
278 snd_pcm_hw_params_alloca(¶ms);
279
280 ret = snd_pcm_hw_params_any(handle, params);
281 if (ret < 0)
282 return false;
283
284 ret = snd_pcm_hw_params_test_format(handle, params, fmt);
285 if (ret < 0) {
286 igt_debug("Output device doesn't support the format %s\n",
287 snd_pcm_format_name(fmt));
288 return false;
289 }
290
291 ret = snd_pcm_hw_params_test_rate(handle, params, sampling_rate, 0);
292 if (ret < 0) {
293 snd_pcm_hw_params_get_rate_min(params, &min_rate, &min_rate_dir);
294 snd_pcm_hw_params_get_rate_max(params, &max_rate, &max_rate_dir);
295 igt_debug("Output device supports rates between %u and %u, "
296 "requested %d\n",
297 min_rate, max_rate, sampling_rate);
298 return false;
299 }
300
301 ret = snd_pcm_hw_params_test_channels(handle, params, channels);
302 if (ret < 0) {
303 snd_pcm_hw_params_get_channels_min(params, &min_channels);
304 snd_pcm_hw_params_get_channels_max(params, &max_channels);
305 igt_debug("Output device supports between %u and "
306 "%u channels, requested %d\n",
307 min_channels, max_channels, channels);
308 return false;
309 }
310
311 return true;
312 }
313
314 /**
315 * alsa_test_output_configuration:
316 * @alsa: The target alsa structure
317 * @fmt: The format to test
318 * @channels: The number of channels to test
319 * @sampling_rate: The sampling rate to test
320 *
321 * Test the output configuration specified by @channels and @sampling_rate
322 * for the output devices.
323 *
324 * Returns: A boolean indicating whether the test succeeded
325 */
alsa_test_output_configuration(struct alsa * alsa,snd_pcm_format_t fmt,int channels,int sampling_rate)326 bool alsa_test_output_configuration(struct alsa *alsa, snd_pcm_format_t fmt,
327 int channels, int sampling_rate)
328 {
329 snd_pcm_t *handle;
330 bool ret;
331 int i;
332
333 for (i = 0; i < alsa->output_handles_count; i++) {
334 handle = alsa->output_handles[i];
335
336 ret = alsa_test_configuration(handle, fmt, channels, sampling_rate);
337 if (!ret)
338 return false;
339 }
340
341 return true;
342 }
343
344 /**
345 * alsa_configure_output:
346 * @alsa: The target alsa structure
347 * @channels: The number of channels to test
348 * @sampling_rate: The sampling rate to test
349 *
350 * Configure the output devices with the configuration specified by @channels
351 * and @sampling_rate.
352 */
alsa_configure_output(struct alsa * alsa,snd_pcm_format_t fmt,int channels,int sampling_rate)353 void alsa_configure_output(struct alsa *alsa, snd_pcm_format_t fmt,
354 int channels, int sampling_rate)
355 {
356 snd_pcm_t *handle;
357 int ret;
358 int i;
359 int soft_resample = 0; /* Don't allow ALSA to resample */
360 unsigned int latency = 0;
361
362 for (i = 0; i < alsa->output_handles_count; i++) {
363 handle = alsa->output_handles[i];
364
365 ret = snd_pcm_set_params(handle, fmt,
366 SND_PCM_ACCESS_RW_INTERLEAVED,
367 channels, sampling_rate,
368 soft_resample, latency);
369 igt_assert(ret >= 0);
370 }
371
372 alsa->output_format = fmt;
373 alsa->output_channels = channels;
374 alsa->output_sampling_rate = sampling_rate;
375 }
376
377 /**
378 * alsa_register_output_callback:
379 * @alsa: The target alsa structure
380 * @callback: The callback function to call to fill output data
381 * @callback_data: The data pointer to pass to the callback function
382 * @samples_trigger: The required number of samples to trigger the callback
383 *
384 * Register a callback function to be called to fill output data during a run.
385 * The callback is called when @samples_trigger samples are required.
386 *
387 * The callback should return an integer equal to zero for success and negative
388 * for failure.
389 */
alsa_register_output_callback(struct alsa * alsa,int (* callback)(void * data,void * buffer,int samples),void * callback_data,int samples_trigger)390 void alsa_register_output_callback(struct alsa *alsa,
391 int (*callback)(void *data, void *buffer, int samples),
392 void *callback_data, int samples_trigger)
393 {
394 alsa->output_callback = callback;
395 alsa->output_callback_data = callback_data;
396 alsa->output_samples_trigger = samples_trigger;
397 }
398
399 /**
400 * alsa_run:
401 * @alsa: The target alsa structure
402 * @duration_ms: The maximum duration of the run in milliseconds, or -1 for an
403 * infinite duration.
404 *
405 * Run ALSA playback and capture on the input and output devices for at
406 * most @duration_ms milliseconds, calling the registered callbacks when needed.
407 *
408 * Returns: An integer equal to zero for success, positive for a stop caused
409 * by the input callback and negative for failure
410 */
alsa_run(struct alsa * alsa,int duration_ms)411 int alsa_run(struct alsa *alsa, int duration_ms)
412 {
413 snd_pcm_t *handle;
414 char *output_buffer = NULL;
415 int output_limit;
416 int output_total = 0;
417 int output_counts[alsa->output_handles_count];
418 bool output_ready = false;
419 int output_channels;
420 int bytes_per_sample;
421 int output_trigger;
422 bool reached;
423 int index;
424 int count;
425 int avail;
426 int i;
427 int ret;
428
429 output_limit = alsa->output_sampling_rate * duration_ms / 1000;
430 output_channels = alsa->output_channels;
431 bytes_per_sample = snd_pcm_format_physical_width(alsa->output_format) / 8;
432 output_trigger = alsa->output_samples_trigger;
433 output_buffer = malloc(output_channels * output_trigger *
434 bytes_per_sample);
435
436 do {
437 reached = true;
438
439 if (output_limit < 0 || output_total < output_limit) {
440 reached = false;
441
442 if (!output_ready) {
443 for (i = 0; i < alsa->output_handles_count; i++)
444 output_counts[i] = 0;
445
446 ret = alsa->output_callback(alsa->output_callback_data,
447 output_buffer,
448 output_trigger);
449 if (ret < 0)
450 goto complete;
451 }
452
453 for (i = 0; i < alsa->output_handles_count; i++) {
454 handle = alsa->output_handles[i];
455
456 ret = snd_pcm_avail(handle);
457 if (output_counts[i] < output_trigger &&
458 ret > 0) {
459 index = output_counts[i] *
460 output_channels;
461 count = output_trigger -
462 output_counts[i];
463 avail = snd_pcm_avail(handle);
464
465 count = avail < count ? avail : count;
466
467 ret = snd_pcm_writei(handle,
468 &output_buffer[index * bytes_per_sample],
469 count);
470 if (ret < 0) {
471 ret = snd_pcm_recover(handle,
472 ret, 0);
473 if (ret < 0) {
474 igt_debug("snd_pcm_recover after snd_pcm_writei failed");
475 goto complete;
476 }
477 }
478
479 output_counts[i] += ret;
480 } else if (output_counts[i] < output_trigger &&
481 ret < 0) {
482 ret = snd_pcm_recover(handle, ret, 0);
483 if (ret < 0) {
484 igt_debug("snd_pcm_recover failed");
485 goto complete;
486 }
487 }
488 }
489
490 output_ready = false;
491
492 for (i = 0; i < alsa->output_handles_count; i++)
493 if (output_counts[i] < output_trigger)
494 output_ready = true;
495
496 if (!output_ready)
497 output_total += output_trigger;
498
499 }
500 } while (!reached);
501
502 ret = 0;
503
504 complete:
505 free(output_buffer);
506
507 return ret;
508 }
509