1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * This is the test which covers PCM middle layer data transferring using 4 * the virtual pcm test driver (snd-pcmtest). 5 * 6 * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com> 7 */ 8 #include <string.h> 9 #include <alsa/asoundlib.h> 10 #include "../kselftest_harness.h" 11 12 #define CH_NUM 4 13 14 struct pattern_buf { 15 char buf[1024]; 16 int len; 17 }; 18 19 struct pattern_buf patterns[CH_NUM]; 20 21 struct pcmtest_test_params { 22 unsigned long buffer_size; 23 unsigned long period_size; 24 unsigned long channels; 25 unsigned int rate; 26 snd_pcm_access_t access; 27 size_t sec_buf_len; 28 size_t sample_size; 29 int time; 30 snd_pcm_format_t format; 31 }; 32 33 static int read_patterns(void) 34 { 35 FILE *fp, *fpl; 36 int i; 37 char pf[64]; 38 char plf[64]; 39 40 for (i = 0; i < CH_NUM; i++) { 41 sprintf(plf, "/sys/kernel/debug/pcmtest/fill_pattern%d_len", i); 42 fpl = fopen(plf, "r"); 43 if (!fpl) 44 return -1; 45 fscanf(fpl, "%u", &patterns[i].len); 46 fclose(fpl); 47 48 sprintf(pf, "/sys/kernel/debug/pcmtest/fill_pattern%d", i); 49 fp = fopen(pf, "r"); 50 if (!fp) 51 return -1; 52 fread(patterns[i].buf, 1, patterns[i].len, fp); 53 fclose(fp); 54 } 55 56 return 0; 57 } 58 59 static int get_test_results(char *debug_name) 60 { 61 int result; 62 FILE *f; 63 char fname[128]; 64 65 sprintf(fname, "/sys/kernel/debug/pcmtest/%s", debug_name); 66 67 f = fopen(fname, "r"); 68 if (!f) { 69 printf("Failed to open file\n"); 70 return -1; 71 } 72 fscanf(f, "%d", &result); 73 fclose(f); 74 75 return result; 76 } 77 78 static size_t get_sec_buf_len(unsigned int rate, unsigned long channels, snd_pcm_format_t format) 79 { 80 return rate * channels * snd_pcm_format_physical_width(format) / 8; 81 } 82 83 static int setup_handle(snd_pcm_t **handle, snd_pcm_sw_params_t *swparams, 84 snd_pcm_hw_params_t *hwparams, struct pcmtest_test_params *params, 85 int card, snd_pcm_stream_t stream) 86 { 87 char pcm_name[32]; 88 int err; 89 90 sprintf(pcm_name, "hw:%d,0,0", card); 91 err = snd_pcm_open(handle, pcm_name, stream, 0); 92 if (err < 0) 93 return err; 94 snd_pcm_hw_params_any(*handle, hwparams); 95 snd_pcm_hw_params_set_rate_resample(*handle, hwparams, 0); 96 snd_pcm_hw_params_set_access(*handle, hwparams, params->access); 97 snd_pcm_hw_params_set_format(*handle, hwparams, params->format); 98 snd_pcm_hw_params_set_channels(*handle, hwparams, params->channels); 99 snd_pcm_hw_params_set_rate_near(*handle, hwparams, ¶ms->rate, 0); 100 snd_pcm_hw_params_set_period_size_near(*handle, hwparams, ¶ms->period_size, 0); 101 snd_pcm_hw_params_set_buffer_size_near(*handle, hwparams, ¶ms->buffer_size); 102 snd_pcm_hw_params(*handle, hwparams); 103 snd_pcm_sw_params_current(*handle, swparams); 104 105 snd_pcm_hw_params_set_rate_resample(*handle, hwparams, 0); 106 snd_pcm_sw_params_set_avail_min(*handle, swparams, params->period_size); 107 snd_pcm_hw_params_set_buffer_size_near(*handle, hwparams, ¶ms->buffer_size); 108 snd_pcm_hw_params_set_period_size_near(*handle, hwparams, ¶ms->period_size, 0); 109 snd_pcm_sw_params(*handle, swparams); 110 snd_pcm_hw_params(*handle, hwparams); 111 112 return 0; 113 } 114 115 FIXTURE(pcmtest) { 116 int card; 117 snd_pcm_sw_params_t *swparams; 118 snd_pcm_hw_params_t *hwparams; 119 struct pcmtest_test_params params; 120 }; 121 122 FIXTURE_TEARDOWN(pcmtest) { 123 } 124 125 FIXTURE_SETUP(pcmtest) { 126 char *card_name; 127 int err; 128 129 if (geteuid()) 130 SKIP(return, "This test needs root to run!"); 131 132 err = read_patterns(); 133 if (err) 134 SKIP(return, "Can't read patterns. Probably, module isn't loaded"); 135 136 card_name = malloc(127); 137 ASSERT_NE(card_name, NULL); 138 self->params.buffer_size = 16384; 139 self->params.period_size = 4096; 140 self->params.channels = CH_NUM; 141 self->params.rate = 8000; 142 self->params.access = SND_PCM_ACCESS_RW_INTERLEAVED; 143 self->params.format = SND_PCM_FORMAT_S16_LE; 144 self->card = -1; 145 self->params.sample_size = snd_pcm_format_physical_width(self->params.format) / 8; 146 147 self->params.sec_buf_len = get_sec_buf_len(self->params.rate, self->params.channels, 148 self->params.format); 149 self->params.time = 4; 150 151 while (snd_card_next(&self->card) >= 0) { 152 if (self->card == -1) 153 break; 154 snd_card_get_name(self->card, &card_name); 155 if (!strcmp(card_name, "PCM-Test")) 156 break; 157 } 158 free(card_name); 159 ASSERT_NE(self->card, -1); 160 } 161 162 /* 163 * Here we are trying to send the looped monotonically increasing sequence of bytes to the driver. 164 * If our data isn't corrupted, the driver will set the content of 'pc_test' debugfs file to '1' 165 */ 166 TEST_F(pcmtest, playback) { 167 snd_pcm_t *handle; 168 unsigned char *it; 169 size_t write_res; 170 int test_results; 171 int i, cur_ch, pos_in_ch; 172 void *samples; 173 struct pcmtest_test_params *params = &self->params; 174 175 samples = calloc(self->params.sec_buf_len * self->params.time, 1); 176 ASSERT_NE(samples, NULL); 177 178 snd_pcm_sw_params_alloca(&self->swparams); 179 snd_pcm_hw_params_alloca(&self->hwparams); 180 181 ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, params, 182 self->card, SND_PCM_STREAM_PLAYBACK), 0); 183 snd_pcm_format_set_silence(params->format, samples, 184 params->rate * params->channels * params->time); 185 it = samples; 186 for (i = 0; i < self->params.sec_buf_len * params->time; i++) { 187 cur_ch = (i / params->sample_size) % CH_NUM; 188 pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size 189 + (i % params->sample_size); 190 it[i] = patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len]; 191 } 192 write_res = snd_pcm_writei(handle, samples, params->rate * params->time); 193 ASSERT_GE(write_res, 0); 194 195 snd_pcm_close(handle); 196 free(samples); 197 test_results = get_test_results("pc_test"); 198 ASSERT_EQ(test_results, 1); 199 } 200 201 /* 202 * Here we test that the virtual alsa driver returns looped and monotonically increasing sequence 203 * of bytes. In the interleaved mode the buffer will contain samples in the following order: 204 * C0, C1, C2, C3, C0, C1, ... 205 */ 206 TEST_F(pcmtest, capture) { 207 snd_pcm_t *handle; 208 unsigned char *it; 209 size_t read_res; 210 int i, cur_ch, pos_in_ch; 211 void *samples; 212 struct pcmtest_test_params *params = &self->params; 213 214 samples = calloc(self->params.sec_buf_len * self->params.time, 1); 215 ASSERT_NE(samples, NULL); 216 217 snd_pcm_sw_params_alloca(&self->swparams); 218 snd_pcm_hw_params_alloca(&self->hwparams); 219 220 ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, 221 params, self->card, SND_PCM_STREAM_CAPTURE), 0); 222 snd_pcm_format_set_silence(params->format, samples, 223 params->rate * params->channels * params->time); 224 read_res = snd_pcm_readi(handle, samples, params->rate * params->time); 225 ASSERT_GE(read_res, 0); 226 snd_pcm_close(handle); 227 it = (unsigned char *)samples; 228 for (i = 0; i < self->params.sec_buf_len * self->params.time; i++) { 229 cur_ch = (i / params->sample_size) % CH_NUM; 230 pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size 231 + (i % params->sample_size); 232 ASSERT_EQ(it[i], patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len]); 233 } 234 free(samples); 235 } 236 237 // Test capture in the non-interleaved access mode. The are buffers for each recorded channel 238 TEST_F(pcmtest, ni_capture) { 239 snd_pcm_t *handle; 240 struct pcmtest_test_params params = self->params; 241 char **chan_samples; 242 size_t i, j, read_res; 243 244 chan_samples = calloc(CH_NUM, sizeof(*chan_samples)); 245 ASSERT_NE(chan_samples, NULL); 246 247 snd_pcm_sw_params_alloca(&self->swparams); 248 snd_pcm_hw_params_alloca(&self->hwparams); 249 250 params.access = SND_PCM_ACCESS_RW_NONINTERLEAVED; 251 252 ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, 253 ¶ms, self->card, SND_PCM_STREAM_CAPTURE), 0); 254 255 for (i = 0; i < CH_NUM; i++) 256 chan_samples[i] = calloc(params.sec_buf_len * params.time, 1); 257 258 for (i = 0; i < 1; i++) { 259 read_res = snd_pcm_readn(handle, (void **)chan_samples, params.rate * params.time); 260 ASSERT_GE(read_res, 0); 261 } 262 snd_pcm_close(handle); 263 264 for (i = 0; i < CH_NUM; i++) { 265 for (j = 0; j < params.rate * params.time; j++) 266 ASSERT_EQ(chan_samples[i][j], patterns[i].buf[j % patterns[i].len]); 267 free(chan_samples[i]); 268 } 269 free(chan_samples); 270 } 271 272 TEST_F(pcmtest, ni_playback) { 273 snd_pcm_t *handle; 274 struct pcmtest_test_params params = self->params; 275 char **chan_samples; 276 size_t i, j, read_res; 277 int test_res; 278 279 chan_samples = calloc(CH_NUM, sizeof(*chan_samples)); 280 ASSERT_NE(chan_samples, NULL); 281 282 snd_pcm_sw_params_alloca(&self->swparams); 283 snd_pcm_hw_params_alloca(&self->hwparams); 284 285 params.access = SND_PCM_ACCESS_RW_NONINTERLEAVED; 286 287 ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, 288 ¶ms, self->card, SND_PCM_STREAM_PLAYBACK), 0); 289 290 for (i = 0; i < CH_NUM; i++) { 291 chan_samples[i] = calloc(params.sec_buf_len * params.time, 1); 292 for (j = 0; j < params.sec_buf_len * params.time; j++) 293 chan_samples[i][j] = patterns[i].buf[j % patterns[i].len]; 294 } 295 296 for (i = 0; i < 1; i++) { 297 read_res = snd_pcm_writen(handle, (void **)chan_samples, params.rate * params.time); 298 ASSERT_GE(read_res, 0); 299 } 300 301 snd_pcm_close(handle); 302 test_res = get_test_results("pc_test"); 303 ASSERT_EQ(test_res, 1); 304 305 for (i = 0; i < CH_NUM; i++) 306 free(chan_samples[i]); 307 free(chan_samples); 308 } 309 310 /* 311 * Here we are testing the custom ioctl definition inside the virtual driver. If it triggers 312 * successfully, the driver sets the content of 'ioctl_test' debugfs file to '1'. 313 */ 314 TEST_F(pcmtest, reset_ioctl) { 315 snd_pcm_t *handle; 316 int test_res; 317 struct pcmtest_test_params *params = &self->params; 318 319 snd_pcm_sw_params_alloca(&self->swparams); 320 snd_pcm_hw_params_alloca(&self->hwparams); 321 322 ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, params, 323 self->card, SND_PCM_STREAM_CAPTURE), 0); 324 snd_pcm_reset(handle); 325 test_res = get_test_results("ioctl_test"); 326 ASSERT_EQ(test_res, 1); 327 snd_pcm_close(handle); 328 } 329 330 TEST_HARNESS_MAIN 331
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.