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