1*aba51cd0SJaroslav Kysela // SPDX-License-Identifier: GPL-2.0 2*aba51cd0SJaroslav Kysela // 3*aba51cd0SJaroslav Kysela // kselftest for the ALSA PCM API 4*aba51cd0SJaroslav Kysela // 5*aba51cd0SJaroslav Kysela // Original author: Jaroslav Kysela <perex@perex.cz> 6*aba51cd0SJaroslav Kysela // Copyright (c) 2022 Red Hat Inc. 7*aba51cd0SJaroslav Kysela 8*aba51cd0SJaroslav Kysela // This test will iterate over all cards detected in the system, exercising 9*aba51cd0SJaroslav Kysela // every PCM device it can find. This may conflict with other system 10*aba51cd0SJaroslav Kysela // software if there is audio activity so is best run on a system with a 11*aba51cd0SJaroslav Kysela // minimal active userspace. 12*aba51cd0SJaroslav Kysela 13*aba51cd0SJaroslav Kysela #include <stdio.h> 14*aba51cd0SJaroslav Kysela #include <stdlib.h> 15*aba51cd0SJaroslav Kysela #include <stdbool.h> 16*aba51cd0SJaroslav Kysela #include <errno.h> 17*aba51cd0SJaroslav Kysela #include <assert.h> 18*aba51cd0SJaroslav Kysela 19*aba51cd0SJaroslav Kysela #include "../kselftest.h" 20*aba51cd0SJaroslav Kysela #include "alsa-local.h" 21*aba51cd0SJaroslav Kysela 22*aba51cd0SJaroslav Kysela typedef struct timespec timestamp_t; 23*aba51cd0SJaroslav Kysela 24*aba51cd0SJaroslav Kysela struct pcm_data { 25*aba51cd0SJaroslav Kysela snd_pcm_t *handle; 26*aba51cd0SJaroslav Kysela int card; 27*aba51cd0SJaroslav Kysela int device; 28*aba51cd0SJaroslav Kysela int subdevice; 29*aba51cd0SJaroslav Kysela snd_pcm_stream_t stream; 30*aba51cd0SJaroslav Kysela snd_config_t *pcm_config; 31*aba51cd0SJaroslav Kysela struct pcm_data *next; 32*aba51cd0SJaroslav Kysela }; 33*aba51cd0SJaroslav Kysela 34*aba51cd0SJaroslav Kysela static const char *alsa_config = 35*aba51cd0SJaroslav Kysela "ctl.hw {\n" 36*aba51cd0SJaroslav Kysela " @args [ CARD ]\n" 37*aba51cd0SJaroslav Kysela " @args.CARD.type string\n" 38*aba51cd0SJaroslav Kysela " type hw\n" 39*aba51cd0SJaroslav Kysela " card $CARD\n" 40*aba51cd0SJaroslav Kysela "}\n" 41*aba51cd0SJaroslav Kysela "pcm.hw {\n" 42*aba51cd0SJaroslav Kysela " @args [ CARD DEV SUBDEV ]\n" 43*aba51cd0SJaroslav Kysela " @args.CARD.type string\n" 44*aba51cd0SJaroslav Kysela " @args.DEV.type integer\n" 45*aba51cd0SJaroslav Kysela " @args.SUBDEV.type integer\n" 46*aba51cd0SJaroslav Kysela " type hw\n" 47*aba51cd0SJaroslav Kysela " card $CARD\n" 48*aba51cd0SJaroslav Kysela " device $DEV\n" 49*aba51cd0SJaroslav Kysela " subdevice $SUBDEV\n" 50*aba51cd0SJaroslav Kysela "}\n" 51*aba51cd0SJaroslav Kysela 52*aba51cd0SJaroslav Kysela ; 53*aba51cd0SJaroslav Kysela 54*aba51cd0SJaroslav Kysela int num_pcms = 0; 55*aba51cd0SJaroslav Kysela struct pcm_data *pcm_list = NULL; 56*aba51cd0SJaroslav Kysela 57*aba51cd0SJaroslav Kysela int num_missing = 0; 58*aba51cd0SJaroslav Kysela struct pcm_data *pcm_missing = NULL; 59*aba51cd0SJaroslav Kysela 60*aba51cd0SJaroslav Kysela void timestamp_now(timestamp_t *tstamp) 61*aba51cd0SJaroslav Kysela { 62*aba51cd0SJaroslav Kysela if (clock_gettime(CLOCK_MONOTONIC_RAW, tstamp)) 63*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("clock_get_time\n"); 64*aba51cd0SJaroslav Kysela } 65*aba51cd0SJaroslav Kysela 66*aba51cd0SJaroslav Kysela long long timestamp_diff_ms(timestamp_t *tstamp) 67*aba51cd0SJaroslav Kysela { 68*aba51cd0SJaroslav Kysela timestamp_t now, diff; 69*aba51cd0SJaroslav Kysela timestamp_now(&now); 70*aba51cd0SJaroslav Kysela if (tstamp->tv_nsec > now.tv_nsec) { 71*aba51cd0SJaroslav Kysela diff.tv_sec = now.tv_sec - tstamp->tv_sec - 1; 72*aba51cd0SJaroslav Kysela diff.tv_nsec = (now.tv_nsec + 1000000000L) - tstamp->tv_nsec; 73*aba51cd0SJaroslav Kysela } else { 74*aba51cd0SJaroslav Kysela diff.tv_sec = now.tv_sec - tstamp->tv_sec; 75*aba51cd0SJaroslav Kysela diff.tv_nsec = now.tv_nsec - tstamp->tv_nsec; 76*aba51cd0SJaroslav Kysela } 77*aba51cd0SJaroslav Kysela return (diff.tv_sec * 1000) + ((diff.tv_nsec + 500000L) / 1000000L); 78*aba51cd0SJaroslav Kysela } 79*aba51cd0SJaroslav Kysela 80*aba51cd0SJaroslav Kysela #ifdef SND_LIB_VER 81*aba51cd0SJaroslav Kysela #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6) 82*aba51cd0SJaroslav Kysela #define LIB_HAS_LOAD_STRING 83*aba51cd0SJaroslav Kysela #endif 84*aba51cd0SJaroslav Kysela #endif 85*aba51cd0SJaroslav Kysela 86*aba51cd0SJaroslav Kysela #ifndef LIB_HAS_LOAD_STRING 87*aba51cd0SJaroslav Kysela static int snd_config_load_string(snd_config_t **config, const char *s, 88*aba51cd0SJaroslav Kysela size_t size) 89*aba51cd0SJaroslav Kysela { 90*aba51cd0SJaroslav Kysela snd_input_t *input; 91*aba51cd0SJaroslav Kysela snd_config_t *dst; 92*aba51cd0SJaroslav Kysela int err; 93*aba51cd0SJaroslav Kysela 94*aba51cd0SJaroslav Kysela assert(config && s); 95*aba51cd0SJaroslav Kysela if (size == 0) 96*aba51cd0SJaroslav Kysela size = strlen(s); 97*aba51cd0SJaroslav Kysela err = snd_input_buffer_open(&input, s, size); 98*aba51cd0SJaroslav Kysela if (err < 0) 99*aba51cd0SJaroslav Kysela return err; 100*aba51cd0SJaroslav Kysela err = snd_config_top(&dst); 101*aba51cd0SJaroslav Kysela if (err < 0) { 102*aba51cd0SJaroslav Kysela snd_input_close(input); 103*aba51cd0SJaroslav Kysela return err; 104*aba51cd0SJaroslav Kysela } 105*aba51cd0SJaroslav Kysela err = snd_config_load(dst, input); 106*aba51cd0SJaroslav Kysela snd_input_close(input); 107*aba51cd0SJaroslav Kysela if (err < 0) { 108*aba51cd0SJaroslav Kysela snd_config_delete(dst); 109*aba51cd0SJaroslav Kysela return err; 110*aba51cd0SJaroslav Kysela } 111*aba51cd0SJaroslav Kysela *config = dst; 112*aba51cd0SJaroslav Kysela return 0; 113*aba51cd0SJaroslav Kysela } 114*aba51cd0SJaroslav Kysela #endif 115*aba51cd0SJaroslav Kysela 116*aba51cd0SJaroslav Kysela static snd_config_t *get_alsalib_config(void) 117*aba51cd0SJaroslav Kysela { 118*aba51cd0SJaroslav Kysela snd_config_t *config; 119*aba51cd0SJaroslav Kysela int err; 120*aba51cd0SJaroslav Kysela 121*aba51cd0SJaroslav Kysela err = snd_config_load_string(&config, alsa_config, strlen(alsa_config)); 122*aba51cd0SJaroslav Kysela if (err < 0) { 123*aba51cd0SJaroslav Kysela ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n", 124*aba51cd0SJaroslav Kysela snd_strerror(err)); 125*aba51cd0SJaroslav Kysela ksft_exit_fail(); 126*aba51cd0SJaroslav Kysela } 127*aba51cd0SJaroslav Kysela return config; 128*aba51cd0SJaroslav Kysela } 129*aba51cd0SJaroslav Kysela 130*aba51cd0SJaroslav Kysela static long device_from_id(snd_config_t *node) 131*aba51cd0SJaroslav Kysela { 132*aba51cd0SJaroslav Kysela const char *id; 133*aba51cd0SJaroslav Kysela char *end; 134*aba51cd0SJaroslav Kysela long v; 135*aba51cd0SJaroslav Kysela 136*aba51cd0SJaroslav Kysela if (snd_config_get_id(node, &id)) 137*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("snd_config_get_id\n"); 138*aba51cd0SJaroslav Kysela errno = 0; 139*aba51cd0SJaroslav Kysela v = strtol(id, &end, 10); 140*aba51cd0SJaroslav Kysela if (errno || *end) 141*aba51cd0SJaroslav Kysela return -1; 142*aba51cd0SJaroslav Kysela return v; 143*aba51cd0SJaroslav Kysela } 144*aba51cd0SJaroslav Kysela 145*aba51cd0SJaroslav Kysela static void missing_device(int card, int device, int subdevice, snd_pcm_stream_t stream) 146*aba51cd0SJaroslav Kysela { 147*aba51cd0SJaroslav Kysela struct pcm_data *pcm_data; 148*aba51cd0SJaroslav Kysela 149*aba51cd0SJaroslav Kysela for (pcm_data = pcm_list; pcm_data != NULL; pcm_data = pcm_data->next) { 150*aba51cd0SJaroslav Kysela if (pcm_data->card != card) 151*aba51cd0SJaroslav Kysela continue; 152*aba51cd0SJaroslav Kysela if (pcm_data->device != device) 153*aba51cd0SJaroslav Kysela continue; 154*aba51cd0SJaroslav Kysela if (pcm_data->subdevice != subdevice) 155*aba51cd0SJaroslav Kysela continue; 156*aba51cd0SJaroslav Kysela if (pcm_data->stream != stream) 157*aba51cd0SJaroslav Kysela continue; 158*aba51cd0SJaroslav Kysela return; 159*aba51cd0SJaroslav Kysela } 160*aba51cd0SJaroslav Kysela pcm_data = calloc(1, sizeof(*pcm_data)); 161*aba51cd0SJaroslav Kysela if (!pcm_data) 162*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("Out of memory\n"); 163*aba51cd0SJaroslav Kysela pcm_data->card = card; 164*aba51cd0SJaroslav Kysela pcm_data->device = device; 165*aba51cd0SJaroslav Kysela pcm_data->subdevice = subdevice; 166*aba51cd0SJaroslav Kysela pcm_data->stream = stream; 167*aba51cd0SJaroslav Kysela pcm_data->next = pcm_missing; 168*aba51cd0SJaroslav Kysela pcm_missing = pcm_data; 169*aba51cd0SJaroslav Kysela num_missing++; 170*aba51cd0SJaroslav Kysela } 171*aba51cd0SJaroslav Kysela 172*aba51cd0SJaroslav Kysela static void missing_devices(int card, snd_config_t *card_config) 173*aba51cd0SJaroslav Kysela { 174*aba51cd0SJaroslav Kysela snd_config_t *pcm_config, *node1, *node2; 175*aba51cd0SJaroslav Kysela snd_config_iterator_t i1, i2, next1, next2; 176*aba51cd0SJaroslav Kysela int device, subdevice; 177*aba51cd0SJaroslav Kysela 178*aba51cd0SJaroslav Kysela pcm_config = conf_get_subtree(card_config, "pcm", NULL); 179*aba51cd0SJaroslav Kysela if (!pcm_config) 180*aba51cd0SJaroslav Kysela return; 181*aba51cd0SJaroslav Kysela snd_config_for_each(i1, next1, pcm_config) { 182*aba51cd0SJaroslav Kysela node1 = snd_config_iterator_entry(i1); 183*aba51cd0SJaroslav Kysela device = device_from_id(node1); 184*aba51cd0SJaroslav Kysela if (device < 0) 185*aba51cd0SJaroslav Kysela continue; 186*aba51cd0SJaroslav Kysela if (snd_config_get_type(node1) != SND_CONFIG_TYPE_COMPOUND) 187*aba51cd0SJaroslav Kysela continue; 188*aba51cd0SJaroslav Kysela snd_config_for_each(i2, next2, node1) { 189*aba51cd0SJaroslav Kysela node2 = snd_config_iterator_entry(i2); 190*aba51cd0SJaroslav Kysela subdevice = device_from_id(node2); 191*aba51cd0SJaroslav Kysela if (subdevice < 0) 192*aba51cd0SJaroslav Kysela continue; 193*aba51cd0SJaroslav Kysela if (conf_get_subtree(node2, "PLAYBACK", NULL)) 194*aba51cd0SJaroslav Kysela missing_device(card, device, subdevice, SND_PCM_STREAM_PLAYBACK); 195*aba51cd0SJaroslav Kysela if (conf_get_subtree(node2, "CAPTURE", NULL)) 196*aba51cd0SJaroslav Kysela missing_device(card, device, subdevice, SND_PCM_STREAM_CAPTURE); 197*aba51cd0SJaroslav Kysela } 198*aba51cd0SJaroslav Kysela } 199*aba51cd0SJaroslav Kysela } 200*aba51cd0SJaroslav Kysela 201*aba51cd0SJaroslav Kysela static void find_pcms(void) 202*aba51cd0SJaroslav Kysela { 203*aba51cd0SJaroslav Kysela char name[32], key[64]; 204*aba51cd0SJaroslav Kysela int card, dev, subdev, count, direction, err; 205*aba51cd0SJaroslav Kysela snd_pcm_stream_t stream; 206*aba51cd0SJaroslav Kysela struct pcm_data *pcm_data; 207*aba51cd0SJaroslav Kysela snd_ctl_t *handle; 208*aba51cd0SJaroslav Kysela snd_pcm_info_t *pcm_info; 209*aba51cd0SJaroslav Kysela snd_config_t *config, *card_config, *pcm_config; 210*aba51cd0SJaroslav Kysela 211*aba51cd0SJaroslav Kysela snd_pcm_info_alloca(&pcm_info); 212*aba51cd0SJaroslav Kysela 213*aba51cd0SJaroslav Kysela card = -1; 214*aba51cd0SJaroslav Kysela if (snd_card_next(&card) < 0 || card < 0) 215*aba51cd0SJaroslav Kysela return; 216*aba51cd0SJaroslav Kysela 217*aba51cd0SJaroslav Kysela config = get_alsalib_config(); 218*aba51cd0SJaroslav Kysela 219*aba51cd0SJaroslav Kysela while (card >= 0) { 220*aba51cd0SJaroslav Kysela sprintf(name, "hw:%d", card); 221*aba51cd0SJaroslav Kysela 222*aba51cd0SJaroslav Kysela err = snd_ctl_open_lconf(&handle, name, 0, config); 223*aba51cd0SJaroslav Kysela if (err < 0) { 224*aba51cd0SJaroslav Kysela ksft_print_msg("Failed to get hctl for card %d: %s\n", 225*aba51cd0SJaroslav Kysela card, snd_strerror(err)); 226*aba51cd0SJaroslav Kysela goto next_card; 227*aba51cd0SJaroslav Kysela } 228*aba51cd0SJaroslav Kysela 229*aba51cd0SJaroslav Kysela card_config = conf_by_card(card); 230*aba51cd0SJaroslav Kysela 231*aba51cd0SJaroslav Kysela dev = -1; 232*aba51cd0SJaroslav Kysela while (1) { 233*aba51cd0SJaroslav Kysela if (snd_ctl_pcm_next_device(handle, &dev) < 0) 234*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("snd_ctl_pcm_next_device\n"); 235*aba51cd0SJaroslav Kysela if (dev < 0) 236*aba51cd0SJaroslav Kysela break; 237*aba51cd0SJaroslav Kysela 238*aba51cd0SJaroslav Kysela for (direction = 0; direction < 2; direction++) { 239*aba51cd0SJaroslav Kysela stream = direction ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK; 240*aba51cd0SJaroslav Kysela sprintf(key, "pcm.%d.%s", dev, snd_pcm_stream_name(stream)); 241*aba51cd0SJaroslav Kysela pcm_config = conf_get_subtree(card_config, key, NULL); 242*aba51cd0SJaroslav Kysela if (conf_get_bool(card_config, key, "skip", false)) { 243*aba51cd0SJaroslav Kysela ksft_print_msg("skipping pcm %d.%d.%s\n", card, dev, snd_pcm_stream_name(stream)); 244*aba51cd0SJaroslav Kysela continue; 245*aba51cd0SJaroslav Kysela } 246*aba51cd0SJaroslav Kysela snd_pcm_info_set_device(pcm_info, dev); 247*aba51cd0SJaroslav Kysela snd_pcm_info_set_subdevice(pcm_info, 0); 248*aba51cd0SJaroslav Kysela snd_pcm_info_set_stream(pcm_info, stream); 249*aba51cd0SJaroslav Kysela err = snd_ctl_pcm_info(handle, pcm_info); 250*aba51cd0SJaroslav Kysela if (err == -ENOENT) 251*aba51cd0SJaroslav Kysela continue; 252*aba51cd0SJaroslav Kysela if (err < 0) 253*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("snd_ctl_pcm_info: %d:%d:%d\n", 254*aba51cd0SJaroslav Kysela dev, 0, stream); 255*aba51cd0SJaroslav Kysela count = snd_pcm_info_get_subdevices_count(pcm_info); 256*aba51cd0SJaroslav Kysela for (subdev = 0; subdev < count; subdev++) { 257*aba51cd0SJaroslav Kysela sprintf(key, "pcm.%d.%d.%s", dev, subdev, snd_pcm_stream_name(stream)); 258*aba51cd0SJaroslav Kysela if (conf_get_bool(card_config, key, "skip", false)) { 259*aba51cd0SJaroslav Kysela ksft_print_msg("skipping pcm %d.%d.%d.%s\n", card, dev, 260*aba51cd0SJaroslav Kysela subdev, snd_pcm_stream_name(stream)); 261*aba51cd0SJaroslav Kysela continue; 262*aba51cd0SJaroslav Kysela } 263*aba51cd0SJaroslav Kysela pcm_data = calloc(1, sizeof(*pcm_data)); 264*aba51cd0SJaroslav Kysela if (!pcm_data) 265*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("Out of memory\n"); 266*aba51cd0SJaroslav Kysela pcm_data->card = card; 267*aba51cd0SJaroslav Kysela pcm_data->device = dev; 268*aba51cd0SJaroslav Kysela pcm_data->subdevice = subdev; 269*aba51cd0SJaroslav Kysela pcm_data->stream = stream; 270*aba51cd0SJaroslav Kysela pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL); 271*aba51cd0SJaroslav Kysela pcm_data->next = pcm_list; 272*aba51cd0SJaroslav Kysela pcm_list = pcm_data; 273*aba51cd0SJaroslav Kysela num_pcms++; 274*aba51cd0SJaroslav Kysela } 275*aba51cd0SJaroslav Kysela } 276*aba51cd0SJaroslav Kysela } 277*aba51cd0SJaroslav Kysela 278*aba51cd0SJaroslav Kysela /* check for missing devices */ 279*aba51cd0SJaroslav Kysela missing_devices(card, card_config); 280*aba51cd0SJaroslav Kysela 281*aba51cd0SJaroslav Kysela next_card: 282*aba51cd0SJaroslav Kysela snd_ctl_close(handle); 283*aba51cd0SJaroslav Kysela if (snd_card_next(&card) < 0) { 284*aba51cd0SJaroslav Kysela ksft_print_msg("snd_card_next"); 285*aba51cd0SJaroslav Kysela break; 286*aba51cd0SJaroslav Kysela } 287*aba51cd0SJaroslav Kysela } 288*aba51cd0SJaroslav Kysela 289*aba51cd0SJaroslav Kysela snd_config_delete(config); 290*aba51cd0SJaroslav Kysela } 291*aba51cd0SJaroslav Kysela 292*aba51cd0SJaroslav Kysela static void test_pcm_time1(struct pcm_data *data, 293*aba51cd0SJaroslav Kysela const char *cfg_prefix, const char *sformat, 294*aba51cd0SJaroslav Kysela long srate, long schannels, 295*aba51cd0SJaroslav Kysela long speriod_size, long sbuffer_size) 296*aba51cd0SJaroslav Kysela { 297*aba51cd0SJaroslav Kysela char name[64], key[128], msg[256]; 298*aba51cd0SJaroslav Kysela const char *cs; 299*aba51cd0SJaroslav Kysela int i, err; 300*aba51cd0SJaroslav Kysela snd_pcm_t *handle = NULL; 301*aba51cd0SJaroslav Kysela snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED; 302*aba51cd0SJaroslav Kysela snd_pcm_format_t format; 303*aba51cd0SJaroslav Kysela unsigned char *samples = NULL; 304*aba51cd0SJaroslav Kysela snd_pcm_sframes_t frames; 305*aba51cd0SJaroslav Kysela long long ms; 306*aba51cd0SJaroslav Kysela long rate, channels, period_size, buffer_size; 307*aba51cd0SJaroslav Kysela unsigned int rrate; 308*aba51cd0SJaroslav Kysela snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold; 309*aba51cd0SJaroslav Kysela timestamp_t tstamp; 310*aba51cd0SJaroslav Kysela bool pass = false, automatic = true; 311*aba51cd0SJaroslav Kysela snd_pcm_hw_params_t *hw_params; 312*aba51cd0SJaroslav Kysela snd_pcm_sw_params_t *sw_params; 313*aba51cd0SJaroslav Kysela 314*aba51cd0SJaroslav Kysela snd_pcm_hw_params_alloca(&hw_params); 315*aba51cd0SJaroslav Kysela snd_pcm_sw_params_alloca(&sw_params); 316*aba51cd0SJaroslav Kysela 317*aba51cd0SJaroslav Kysela cs = conf_get_string(data->pcm_config, cfg_prefix, "format", sformat); 318*aba51cd0SJaroslav Kysela format = snd_pcm_format_value(cs); 319*aba51cd0SJaroslav Kysela if (format == SND_PCM_FORMAT_UNKNOWN) 320*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("Wrong format '%s'\n", cs); 321*aba51cd0SJaroslav Kysela rate = conf_get_long(data->pcm_config, cfg_prefix, "rate", srate); 322*aba51cd0SJaroslav Kysela channels = conf_get_long(data->pcm_config, cfg_prefix, "channels", schannels); 323*aba51cd0SJaroslav Kysela period_size = conf_get_long(data->pcm_config, cfg_prefix, "period_size", speriod_size); 324*aba51cd0SJaroslav Kysela buffer_size = conf_get_long(data->pcm_config, cfg_prefix, "buffer_size", sbuffer_size); 325*aba51cd0SJaroslav Kysela 326*aba51cd0SJaroslav Kysela automatic = strcmp(sformat, snd_pcm_format_name(format)) == 0 && 327*aba51cd0SJaroslav Kysela srate == rate && 328*aba51cd0SJaroslav Kysela schannels == channels && 329*aba51cd0SJaroslav Kysela speriod_size == period_size && 330*aba51cd0SJaroslav Kysela sbuffer_size == buffer_size; 331*aba51cd0SJaroslav Kysela 332*aba51cd0SJaroslav Kysela samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8); 333*aba51cd0SJaroslav Kysela if (!samples) 334*aba51cd0SJaroslav Kysela ksft_exit_fail_msg("Out of memory\n"); 335*aba51cd0SJaroslav Kysela snd_pcm_format_set_silence(format, samples, rate * channels); 336*aba51cd0SJaroslav Kysela 337*aba51cd0SJaroslav Kysela sprintf(name, "hw:%d,%d,%d", data->card, data->device, data->subdevice); 338*aba51cd0SJaroslav Kysela err = snd_pcm_open(&handle, name, data->stream, 0); 339*aba51cd0SJaroslav Kysela if (err < 0) { 340*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "Failed to get pcm handle: %s", snd_strerror(err)); 341*aba51cd0SJaroslav Kysela goto __close; 342*aba51cd0SJaroslav Kysela } 343*aba51cd0SJaroslav Kysela 344*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_any(handle, hw_params); 345*aba51cd0SJaroslav Kysela if (err < 0) { 346*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_any: %s", snd_strerror(err)); 347*aba51cd0SJaroslav Kysela goto __close; 348*aba51cd0SJaroslav Kysela } 349*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0); 350*aba51cd0SJaroslav Kysela if (err < 0) { 351*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate_resample: %s", snd_strerror(err)); 352*aba51cd0SJaroslav Kysela goto __close; 353*aba51cd0SJaroslav Kysela } 354*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_access(handle, hw_params, access); 355*aba51cd0SJaroslav Kysela if (err < 0) { 356*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_access %s: %s", 357*aba51cd0SJaroslav Kysela snd_pcm_access_name(access), snd_strerror(err)); 358*aba51cd0SJaroslav Kysela goto __close; 359*aba51cd0SJaroslav Kysela } 360*aba51cd0SJaroslav Kysela __format: 361*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_format(handle, hw_params, format); 362*aba51cd0SJaroslav Kysela if (err < 0) { 363*aba51cd0SJaroslav Kysela if (automatic && format == SND_PCM_FORMAT_S16_LE) { 364*aba51cd0SJaroslav Kysela format = SND_PCM_FORMAT_S32_LE; 365*aba51cd0SJaroslav Kysela ksft_print_msg("%s.%d.%d.%d.%s.%s format S16_LE -> S32_LE\n", 366*aba51cd0SJaroslav Kysela cfg_prefix, 367*aba51cd0SJaroslav Kysela data->card, data->device, data->subdevice, 368*aba51cd0SJaroslav Kysela snd_pcm_stream_name(data->stream), 369*aba51cd0SJaroslav Kysela snd_pcm_access_name(access)); 370*aba51cd0SJaroslav Kysela } 371*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s", 372*aba51cd0SJaroslav Kysela snd_pcm_format_name(format), snd_strerror(err)); 373*aba51cd0SJaroslav Kysela goto __close; 374*aba51cd0SJaroslav Kysela } 375*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_channels(handle, hw_params, channels); 376*aba51cd0SJaroslav Kysela if (err < 0) { 377*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err)); 378*aba51cd0SJaroslav Kysela goto __close; 379*aba51cd0SJaroslav Kysela } 380*aba51cd0SJaroslav Kysela rrate = rate; 381*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0); 382*aba51cd0SJaroslav Kysela if (err < 0) { 383*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate %ld: %s", rate, snd_strerror(err)); 384*aba51cd0SJaroslav Kysela goto __close; 385*aba51cd0SJaroslav Kysela } 386*aba51cd0SJaroslav Kysela if (rrate != rate) { 387*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "rate mismatch %ld != %ld", rate, rrate); 388*aba51cd0SJaroslav Kysela goto __close; 389*aba51cd0SJaroslav Kysela } 390*aba51cd0SJaroslav Kysela rperiod_size = period_size; 391*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &rperiod_size, 0); 392*aba51cd0SJaroslav Kysela if (err < 0) { 393*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_period_size %ld: %s", period_size, snd_strerror(err)); 394*aba51cd0SJaroslav Kysela goto __close; 395*aba51cd0SJaroslav Kysela } 396*aba51cd0SJaroslav Kysela rbuffer_size = buffer_size; 397*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &rbuffer_size); 398*aba51cd0SJaroslav Kysela if (err < 0) { 399*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_buffer_size %ld: %s", buffer_size, snd_strerror(err)); 400*aba51cd0SJaroslav Kysela goto __close; 401*aba51cd0SJaroslav Kysela } 402*aba51cd0SJaroslav Kysela err = snd_pcm_hw_params(handle, hw_params); 403*aba51cd0SJaroslav Kysela if (err < 0) { 404*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_hw_params: %s", snd_strerror(err)); 405*aba51cd0SJaroslav Kysela goto __close; 406*aba51cd0SJaroslav Kysela } 407*aba51cd0SJaroslav Kysela 408*aba51cd0SJaroslav Kysela err = snd_pcm_sw_params_current(handle, sw_params); 409*aba51cd0SJaroslav Kysela if (err < 0) { 410*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_sw_params_current: %s", snd_strerror(err)); 411*aba51cd0SJaroslav Kysela goto __close; 412*aba51cd0SJaroslav Kysela } 413*aba51cd0SJaroslav Kysela if (data->stream == SND_PCM_STREAM_PLAYBACK) { 414*aba51cd0SJaroslav Kysela start_threshold = (rbuffer_size / rperiod_size) * rperiod_size; 415*aba51cd0SJaroslav Kysela } else { 416*aba51cd0SJaroslav Kysela start_threshold = rperiod_size; 417*aba51cd0SJaroslav Kysela } 418*aba51cd0SJaroslav Kysela err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, start_threshold); 419*aba51cd0SJaroslav Kysela if (err < 0) { 420*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_start_threshold %ld: %s", (long)start_threshold, snd_strerror(err)); 421*aba51cd0SJaroslav Kysela goto __close; 422*aba51cd0SJaroslav Kysela } 423*aba51cd0SJaroslav Kysela err = snd_pcm_sw_params_set_avail_min(handle, sw_params, rperiod_size); 424*aba51cd0SJaroslav Kysela if (err < 0) { 425*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_avail_min %ld: %s", (long)rperiod_size, snd_strerror(err)); 426*aba51cd0SJaroslav Kysela goto __close; 427*aba51cd0SJaroslav Kysela } 428*aba51cd0SJaroslav Kysela err = snd_pcm_sw_params(handle, sw_params); 429*aba51cd0SJaroslav Kysela if (err < 0) { 430*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "snd_pcm_sw_params: %s", snd_strerror(err)); 431*aba51cd0SJaroslav Kysela goto __close; 432*aba51cd0SJaroslav Kysela } 433*aba51cd0SJaroslav Kysela 434*aba51cd0SJaroslav Kysela ksft_print_msg("%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n", 435*aba51cd0SJaroslav Kysela cfg_prefix, 436*aba51cd0SJaroslav Kysela data->card, data->device, data->subdevice, 437*aba51cd0SJaroslav Kysela snd_pcm_stream_name(data->stream), 438*aba51cd0SJaroslav Kysela snd_pcm_access_name(access), 439*aba51cd0SJaroslav Kysela snd_pcm_format_name(format), 440*aba51cd0SJaroslav Kysela (long)rate, (long)channels, 441*aba51cd0SJaroslav Kysela (long)rperiod_size, (long)rbuffer_size, 442*aba51cd0SJaroslav Kysela (long)start_threshold); 443*aba51cd0SJaroslav Kysela 444*aba51cd0SJaroslav Kysela timestamp_now(&tstamp); 445*aba51cd0SJaroslav Kysela for (i = 0; i < 4; i++) { 446*aba51cd0SJaroslav Kysela if (data->stream == SND_PCM_STREAM_PLAYBACK) { 447*aba51cd0SJaroslav Kysela frames = snd_pcm_writei(handle, samples, rate); 448*aba51cd0SJaroslav Kysela if (frames < 0) { 449*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), 450*aba51cd0SJaroslav Kysela "Write failed: expected %d, wrote %li", rate, frames); 451*aba51cd0SJaroslav Kysela goto __close; 452*aba51cd0SJaroslav Kysela } 453*aba51cd0SJaroslav Kysela if (frames < rate) { 454*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), 455*aba51cd0SJaroslav Kysela "expected %d, wrote %li", rate, frames); 456*aba51cd0SJaroslav Kysela goto __close; 457*aba51cd0SJaroslav Kysela } 458*aba51cd0SJaroslav Kysela } else { 459*aba51cd0SJaroslav Kysela frames = snd_pcm_readi(handle, samples, rate); 460*aba51cd0SJaroslav Kysela if (frames < 0) { 461*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), 462*aba51cd0SJaroslav Kysela "expected %d, wrote %li", rate, frames); 463*aba51cd0SJaroslav Kysela goto __close; 464*aba51cd0SJaroslav Kysela } 465*aba51cd0SJaroslav Kysela if (frames < rate) { 466*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), 467*aba51cd0SJaroslav Kysela "expected %d, wrote %li", rate, frames); 468*aba51cd0SJaroslav Kysela goto __close; 469*aba51cd0SJaroslav Kysela } 470*aba51cd0SJaroslav Kysela } 471*aba51cd0SJaroslav Kysela } 472*aba51cd0SJaroslav Kysela 473*aba51cd0SJaroslav Kysela snd_pcm_drain(handle); 474*aba51cd0SJaroslav Kysela ms = timestamp_diff_ms(&tstamp); 475*aba51cd0SJaroslav Kysela if (ms < 3900 || ms > 4100) { 476*aba51cd0SJaroslav Kysela snprintf(msg, sizeof(msg), "time mismatch: expected 4000ms got %lld", ms); 477*aba51cd0SJaroslav Kysela goto __close; 478*aba51cd0SJaroslav Kysela } 479*aba51cd0SJaroslav Kysela 480*aba51cd0SJaroslav Kysela msg[0] = '\0'; 481*aba51cd0SJaroslav Kysela pass = true; 482*aba51cd0SJaroslav Kysela __close: 483*aba51cd0SJaroslav Kysela ksft_test_result(pass, "%s.%d.%d.%d.%s%s%s\n", 484*aba51cd0SJaroslav Kysela cfg_prefix, 485*aba51cd0SJaroslav Kysela data->card, data->device, data->subdevice, 486*aba51cd0SJaroslav Kysela snd_pcm_stream_name(data->stream), 487*aba51cd0SJaroslav Kysela msg[0] ? " " : "", msg); 488*aba51cd0SJaroslav Kysela free(samples); 489*aba51cd0SJaroslav Kysela if (handle) 490*aba51cd0SJaroslav Kysela snd_pcm_close(handle); 491*aba51cd0SJaroslav Kysela } 492*aba51cd0SJaroslav Kysela 493*aba51cd0SJaroslav Kysela #define TESTS_PER_PCM 2 494*aba51cd0SJaroslav Kysela 495*aba51cd0SJaroslav Kysela int main(void) 496*aba51cd0SJaroslav Kysela { 497*aba51cd0SJaroslav Kysela struct pcm_data *pcm; 498*aba51cd0SJaroslav Kysela 499*aba51cd0SJaroslav Kysela ksft_print_header(); 500*aba51cd0SJaroslav Kysela 501*aba51cd0SJaroslav Kysela conf_load(); 502*aba51cd0SJaroslav Kysela 503*aba51cd0SJaroslav Kysela find_pcms(); 504*aba51cd0SJaroslav Kysela 505*aba51cd0SJaroslav Kysela ksft_set_plan(num_missing + num_pcms * TESTS_PER_PCM); 506*aba51cd0SJaroslav Kysela 507*aba51cd0SJaroslav Kysela for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) { 508*aba51cd0SJaroslav Kysela ksft_test_result(false, "test.missing.%d.%d.%d.%s\n", 509*aba51cd0SJaroslav Kysela pcm->card, pcm->device, pcm->subdevice, 510*aba51cd0SJaroslav Kysela snd_pcm_stream_name(pcm->stream)); 511*aba51cd0SJaroslav Kysela } 512*aba51cd0SJaroslav Kysela 513*aba51cd0SJaroslav Kysela for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { 514*aba51cd0SJaroslav Kysela test_pcm_time1(pcm, "test.time1", "S16_LE", 48000, 2, 512, 4096); 515*aba51cd0SJaroslav Kysela test_pcm_time1(pcm, "test.time2", "S16_LE", 48000, 2, 24000, 192000); 516*aba51cd0SJaroslav Kysela } 517*aba51cd0SJaroslav Kysela 518*aba51cd0SJaroslav Kysela conf_free(); 519*aba51cd0SJaroslav Kysela 520*aba51cd0SJaroslav Kysela ksft_exit_pass(); 521*aba51cd0SJaroslav Kysela 522*aba51cd0SJaroslav Kysela return 0; 523*aba51cd0SJaroslav Kysela } 524