1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Virtual ALSA driver for PCM testing/fuzzing 4 * 5 * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com> 6 * 7 * This is a simple virtual ALSA driver, which can be used for audio applications/PCM middle layer 8 * testing or fuzzing. 9 * It can: 10 * - Simulate 'playback' and 'capture' actions 11 * - Generate random or pattern-based capture data 12 * - Check playback buffer for containing looped template, and notify about the results 13 * through the debugfs entry 14 * - Inject delays into the playback and capturing processes. See 'inject_delay' parameter. 15 * - Inject errors during the PCM callbacks. 16 * - Register custom RESET ioctl and notify when it is called through the debugfs entry 17 * - Work in interleaved and non-interleaved modes 18 * - Support up to 8 substreams 19 * - Support up to 4 channels 20 * - Support framerates from 8 kHz to 48 kHz 21 * 22 * When driver works in the capture mode with multiple channels, it duplicates the looped 23 * pattern to each separate channel. For example, if we have 2 channels, format = U8, interleaved 24 * access mode and pattern 'abacaba', the DMA buffer will look like aabbccaabbaaaa..., so buffer for 25 * each channel will contain abacabaabacaba... Same for the non-interleaved mode. 26 * 27 * However, it may break the capturing on the higher framerates with small period size, so it is 28 * better to choose larger period sizes. 29 * 30 * You can find the corresponding selftest in the 'alsa' selftests folder. 31 */ 32 33 #include <linux/module.h> 34 #include <linux/init.h> 35 #include <sound/pcm.h> 36 #include <sound/core.h> 37 #include <linux/dma-mapping.h> 38 #include <linux/platform_device.h> 39 #include <linux/timer.h> 40 #include <linux/random.h> 41 #include <linux/debugfs.h> 42 #include <linux/delay.h> 43 44 #define TIMER_PER_SEC 5 45 #define TIMER_INTERVAL (HZ / TIMER_PER_SEC) 46 #define DELAY_JIFFIES HZ 47 #define PLAYBACK_SUBSTREAM_CNT 8 48 #define CAPTURE_SUBSTREAM_CNT 8 49 #define MAX_CHANNELS_NUM 4 50 51 #define DEFAULT_PATTERN "abacaba" 52 #define DEFAULT_PATTERN_LEN 7 53 54 #define FILL_MODE_RAND 0 55 #define FILL_MODE_PAT 1 56 57 #define MAX_PATTERN_LEN 4096 58 59 static int index = -1; 60 static char *id = "pcmtest"; 61 static bool enable = true; 62 static int inject_delay; 63 static bool inject_hwpars_err; 64 static bool inject_prepare_err; 65 static bool inject_trigger_err; 66 static bool inject_open_err; 67 68 static short fill_mode = FILL_MODE_PAT; 69 70 static u8 playback_capture_test; 71 static u8 ioctl_reset_test; 72 static struct dentry *driver_debug_dir; 73 74 module_param(index, int, 0444); 75 MODULE_PARM_DESC(index, "Index value for pcmtest soundcard"); 76 module_param(id, charp, 0444); 77 MODULE_PARM_DESC(id, "ID string for pcmtest soundcard"); 78 module_param(enable, bool, 0444); 79 MODULE_PARM_DESC(enable, "Enable pcmtest soundcard."); 80 module_param(fill_mode, short, 0600); 81 MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)"); 82 module_param(inject_delay, int, 0600); 83 MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)"); 84 module_param(inject_hwpars_err, bool, 0600); 85 MODULE_PARM_DESC(inject_hwpars_err, "Inject EBUSY error in the 'hw_params' callback"); 86 module_param(inject_prepare_err, bool, 0600); 87 MODULE_PARM_DESC(inject_prepare_err, "Inject EINVAL error in the 'prepare' callback"); 88 module_param(inject_trigger_err, bool, 0600); 89 MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback"); 90 module_param(inject_open_err, bool, 0600); 91 MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback"); 92 93 struct pcmtst { 94 struct snd_pcm *pcm; 95 struct snd_card *card; 96 struct platform_device *pdev; 97 }; 98 99 struct pcmtst_buf_iter { 100 size_t buf_pos; // position in the DMA buffer 101 size_t period_pos; // period-relative position 102 size_t b_rw; // Bytes to write on every timer tick 103 size_t s_rw_ch; // Samples to write to one channel on every tick 104 unsigned int sample_bytes; // sample_bits / 8 105 bool is_buf_corrupted; // playback test result indicator 106 size_t period_bytes; // bytes in a one period 107 bool interleaved; // Interleaved/Non-interleaved mode 108 size_t total_bytes; // Total bytes read/written 109 size_t chan_block; // Bytes in one channel buffer when non-interleaved 110 struct snd_pcm_substream *substream; 111 bool suspend; // We need to pause timer without shutting it down 112 struct timer_list timer_instance; 113 }; 114 115 static struct snd_pcm_hardware snd_pcmtst_hw = { 116 .info = (SNDRV_PCM_INFO_INTERLEAVED | 117 SNDRV_PCM_INFO_BLOCK_TRANSFER | 118 SNDRV_PCM_INFO_NONINTERLEAVED | 119 SNDRV_PCM_INFO_MMAP_VALID | 120 SNDRV_PCM_INFO_PAUSE), 121 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 122 .rates = SNDRV_PCM_RATE_8000_48000, 123 .rate_min = 8000, 124 .rate_max = 48000, 125 .channels_min = 1, 126 .channels_max = MAX_CHANNELS_NUM, 127 .buffer_bytes_max = 128 * 1024, 128 .period_bytes_min = 4096, 129 .period_bytes_max = 32768, 130 .periods_min = 1, 131 .periods_max = 1024, 132 }; 133 134 struct pattern_buf { 135 char *buf; 136 u32 len; 137 }; 138 139 static int buf_allocated; 140 static struct pattern_buf patt_bufs[MAX_CHANNELS_NUM]; 141 142 static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t bytes) 143 { 144 v_iter->total_bytes += by; 145 v_iter->buf_pos += by; 146 if (v_iter->buf_pos >= bytes) 147 v_iter->buf_pos %= bytes; 148 } 149 150 /* 151 * Position in the DMA buffer when we are in the non-interleaved mode. We increment buf_pos 152 * every time we write a byte to any channel, so the position in the current channel buffer is 153 * (position in the DMA buffer) / count_of_channels + size_of_channel_buf * current_channel 154 */ 155 static inline size_t buf_pos_n(struct pcmtst_buf_iter *v_iter, unsigned int channels, 156 unsigned int chan_num) 157 { 158 return v_iter->buf_pos / channels + v_iter->chan_block * chan_num; 159 } 160 161 /* 162 * Get the count of bytes written for the current channel in the interleaved mode. 163 * This is (count of samples written for the current channel) * bytes_in_sample + 164 * (relative position in the current sample) 165 */ 166 static inline size_t ch_pos_i(size_t b_total, unsigned int channels, unsigned int b_sample) 167 { 168 return b_total / channels / b_sample * b_sample + (b_total % b_sample); 169 } 170 171 static void check_buf_block_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 172 { 173 size_t i; 174 short ch_num; 175 u8 current_byte; 176 177 for (i = 0; i < v_iter->b_rw; i++) { 178 current_byte = runtime->dma_area[v_iter->buf_pos]; 179 if (!current_byte) 180 break; 181 ch_num = (v_iter->total_bytes / v_iter->sample_bytes) % runtime->channels; 182 if (current_byte != patt_bufs[ch_num].buf[ch_pos_i(v_iter->total_bytes, 183 runtime->channels, 184 v_iter->sample_bytes) 185 % patt_bufs[ch_num].len]) { 186 v_iter->is_buf_corrupted = true; 187 break; 188 } 189 inc_buf_pos(v_iter, 1, runtime->dma_bytes); 190 } 191 // If we broke during the loop, add remaining bytes to the buffer position. 192 inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes); 193 } 194 195 static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 196 { 197 unsigned int channels = runtime->channels; 198 size_t i; 199 short ch_num; 200 u8 current_byte; 201 202 for (i = 0; i < v_iter->b_rw; i++) { 203 ch_num = i % channels; 204 current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)]; 205 if (!current_byte) 206 break; 207 if (current_byte != patt_bufs[ch_num].buf[(v_iter->total_bytes / channels) 208 % patt_bufs[ch_num].len]) { 209 v_iter->is_buf_corrupted = true; 210 break; 211 } 212 inc_buf_pos(v_iter, 1, runtime->dma_bytes); 213 } 214 inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes); 215 } 216 217 /* 218 * Check one block of the buffer. Here we iterate the buffer until we find ''. This condition is 219 * necessary because we need to detect when the reading/writing ends, so we assume that the pattern 220 * doesn't contain zeros. 221 */ 222 static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 223 { 224 if (v_iter->interleaved) 225 check_buf_block_i(v_iter, runtime); 226 else 227 check_buf_block_ni(v_iter, runtime); 228 } 229 230 /* 231 * Fill buffer in the non-interleaved mode. The order of samples is C0, ..., C0, C1, ..., C1, C2... 232 * The channel buffers lay in the DMA buffer continuously (see default copy 233 * handlers in the pcm_lib.c file). 234 * 235 * Here we increment the DMA buffer position every time we write a byte to any channel 'buffer'. 236 * We need this to simulate the correct hardware pointer moving. 237 */ 238 static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 239 { 240 size_t i; 241 unsigned int channels = runtime->channels; 242 short ch_num; 243 244 for (i = 0; i < v_iter->b_rw; i++) { 245 ch_num = i % channels; 246 runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)] = 247 patt_bufs[ch_num].buf[(v_iter->total_bytes / channels) 248 % patt_bufs[ch_num].len]; 249 inc_buf_pos(v_iter, 1, runtime->dma_bytes); 250 } 251 } 252 253 // Fill buffer in the interleaved mode. The order of samples is C0, C1, C2, C0, C1, C2, ... 254 static void fill_block_pattern_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 255 { 256 size_t sample; 257 size_t pos_in_ch, pos_pattern; 258 short ch, pos_sample; 259 260 pos_in_ch = ch_pos_i(v_iter->total_bytes, runtime->channels, v_iter->sample_bytes); 261 262 for (sample = 0; sample < v_iter->s_rw_ch; sample++) { 263 for (ch = 0; ch < runtime->channels; ch++) { 264 for (pos_sample = 0; pos_sample < v_iter->sample_bytes; pos_sample++) { 265 pos_pattern = (pos_in_ch + sample * v_iter->sample_bytes 266 + pos_sample) % patt_bufs[ch].len; 267 runtime->dma_area[v_iter->buf_pos] = patt_bufs[ch].buf[pos_pattern]; 268 inc_buf_pos(v_iter, 1, runtime->dma_bytes); 269 } 270 } 271 } 272 } 273 274 static void fill_block_pattern(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 275 { 276 if (v_iter->interleaved) 277 fill_block_pattern_i(v_iter, runtime); 278 else 279 fill_block_pattern_n(v_iter, runtime); 280 } 281 282 static void fill_block_rand_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 283 { 284 unsigned int channels = runtime->channels; 285 // Remaining space in all channel buffers 286 size_t bytes_remain = runtime->dma_bytes - v_iter->buf_pos; 287 unsigned int i; 288 289 for (i = 0; i < channels; i++) { 290 if (v_iter->b_rw <= bytes_remain) { 291 //b_rw - count of bytes must be written for all channels at each timer tick 292 get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i), 293 v_iter->b_rw / channels); 294 } else { 295 // Write to the end of buffer and start from the beginning of it 296 get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i), 297 bytes_remain / channels); 298 get_random_bytes(runtime->dma_area + v_iter->chan_block * i, 299 (v_iter->b_rw - bytes_remain) / channels); 300 } 301 } 302 inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes); 303 } 304 305 static void fill_block_rand_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 306 { 307 size_t in_cur_block = runtime->dma_bytes - v_iter->buf_pos; 308 309 if (v_iter->b_rw <= in_cur_block) { 310 get_random_bytes(&runtime->dma_area[v_iter->buf_pos], v_iter->b_rw); 311 } else { 312 get_random_bytes(&runtime->dma_area[v_iter->buf_pos], in_cur_block); 313 get_random_bytes(runtime->dma_area, v_iter->b_rw - in_cur_block); 314 } 315 inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes); 316 } 317 318 static void fill_block_random(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 319 { 320 if (v_iter->interleaved) 321 fill_block_rand_i(v_iter, runtime); 322 else 323 fill_block_rand_n(v_iter, runtime); 324 } 325 326 static void fill_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime) 327 { 328 switch (fill_mode) { 329 case FILL_MODE_RAND: 330 fill_block_random(v_iter, runtime); 331 break; 332 case FILL_MODE_PAT: 333 fill_block_pattern(v_iter, runtime); 334 break; 335 } 336 } 337 338 /* 339 * Here we iterate through the buffer by (buffer_size / iterates_per_second) bytes. 340 * The driver uses timer to simulate the hardware pointer moving, and notify the PCM middle layer 341 * about period elapsed. 342 */ 343 static void timer_timeout(struct timer_list *data) 344 { 345 struct pcmtst_buf_iter *v_iter; 346 struct snd_pcm_substream *substream; 347 348 v_iter = from_timer(v_iter, data, timer_instance); 349 substream = v_iter->substream; 350 351 if (v_iter->suspend) 352 return; 353 354 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted) 355 check_buf_block(v_iter, substream->runtime); 356 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 357 fill_block(v_iter, substream->runtime); 358 else 359 inc_buf_pos(v_iter, v_iter->b_rw, substream->runtime->dma_bytes); 360 361 v_iter->period_pos += v_iter->b_rw; 362 if (v_iter->period_pos >= v_iter->period_bytes) { 363 v_iter->period_pos %= v_iter->period_bytes; 364 snd_pcm_period_elapsed(substream); 365 } 366 367 if (!v_iter->suspend) 368 mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay); 369 } 370 371 static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream) 372 { 373 struct snd_pcm_runtime *runtime = substream->runtime; 374 struct pcmtst_buf_iter *v_iter; 375 376 if (inject_open_err) 377 return -EBUSY; 378 379 v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL); 380 if (!v_iter) 381 return -ENOMEM; 382 383 v_iter->substream = substream; 384 runtime->hw = snd_pcmtst_hw; 385 runtime->private_data = v_iter; 386 387 playback_capture_test = 0; 388 ioctl_reset_test = 0; 389 390 timer_setup(&v_iter->timer_instance, timer_timeout, 0); 391 392 return 0; 393 } 394 395 static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream) 396 { 397 struct pcmtst_buf_iter *v_iter = substream->runtime->private_data; 398 399 timer_shutdown_sync(&v_iter->timer_instance); 400 playback_capture_test = !v_iter->is_buf_corrupted; 401 kfree(v_iter); 402 return 0; 403 } 404 405 static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter) 406 { 407 v_iter->buf_pos = 0; 408 v_iter->is_buf_corrupted = false; 409 v_iter->period_pos = 0; 410 v_iter->total_bytes = 0; 411 } 412 413 static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter) 414 { 415 v_iter->suspend = false; 416 mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL); 417 } 418 419 static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 420 { 421 struct pcmtst_buf_iter *v_iter = substream->runtime->private_data; 422 423 if (inject_trigger_err) 424 return -EINVAL; 425 switch (cmd) { 426 case SNDRV_PCM_TRIGGER_START: 427 reset_buf_iterator(v_iter); 428 start_pcmtest_timer(v_iter); 429 break; 430 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 431 start_pcmtest_timer(v_iter); 432 break; 433 case SNDRV_PCM_TRIGGER_STOP: 434 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 435 // We can't call timer_shutdown_sync here, as it is forbidden to sleep here 436 v_iter->suspend = true; 437 timer_delete(&v_iter->timer_instance); 438 break; 439 } 440 441 return 0; 442 } 443 444 static snd_pcm_uframes_t snd_pcmtst_pcm_pointer(struct snd_pcm_substream *substream) 445 { 446 struct pcmtst_buf_iter *v_iter = substream->runtime->private_data; 447 448 return bytes_to_frames(substream->runtime, v_iter->buf_pos); 449 } 450 451 static int snd_pcmtst_free(struct pcmtst *pcmtst) 452 { 453 if (!pcmtst) 454 return 0; 455 kfree(pcmtst); 456 return 0; 457 } 458 459 // These callbacks are required, but empty - all freeing occurs in pdev_remove 460 static int snd_pcmtst_dev_free(struct snd_device *device) 461 { 462 return 0; 463 } 464 465 static void pcmtst_pdev_release(struct device *dev) 466 { 467 } 468 469 static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream) 470 { 471 struct snd_pcm_runtime *runtime = substream->runtime; 472 struct pcmtst_buf_iter *v_iter = runtime->private_data; 473 474 if (inject_prepare_err) 475 return -EINVAL; 476 477 v_iter->sample_bytes = samples_to_bytes(runtime, 1); 478 v_iter->period_bytes = snd_pcm_lib_period_bytes(substream); 479 v_iter->interleaved = true; 480 if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED || 481 runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) { 482 v_iter->chan_block = snd_pcm_lib_buffer_bytes(substream) / runtime->channels; 483 v_iter->interleaved = false; 484 } 485 // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes 486 v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC; 487 v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels; 488 489 return 0; 490 } 491 492 static int snd_pcmtst_pcm_hw_params(struct snd_pcm_substream *substream, 493 struct snd_pcm_hw_params *params) 494 { 495 if (inject_hwpars_err) 496 return -EBUSY; 497 return 0; 498 } 499 500 static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream *substream) 501 { 502 return 0; 503 } 504 505 static int snd_pcmtst_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) 506 { 507 switch (cmd) { 508 case SNDRV_PCM_IOCTL1_RESET: 509 ioctl_reset_test = 1; 510 break; 511 } 512 return snd_pcm_lib_ioctl(substream, cmd, arg); 513 } 514 515 static int snd_pcmtst_sync_stop(struct snd_pcm_substream *substream) 516 { 517 struct pcmtst_buf_iter *v_iter = substream->runtime->private_data; 518 519 timer_delete_sync(&v_iter->timer_instance); 520 521 return 0; 522 } 523 524 static const struct snd_pcm_ops snd_pcmtst_playback_ops = { 525 .open = snd_pcmtst_pcm_open, 526 .close = snd_pcmtst_pcm_close, 527 .trigger = snd_pcmtst_pcm_trigger, 528 .hw_params = snd_pcmtst_pcm_hw_params, 529 .ioctl = snd_pcmtst_ioctl, 530 .sync_stop = snd_pcmtst_sync_stop, 531 .hw_free = snd_pcmtst_pcm_hw_free, 532 .prepare = snd_pcmtst_pcm_prepare, 533 .pointer = snd_pcmtst_pcm_pointer, 534 }; 535 536 static const struct snd_pcm_ops snd_pcmtst_capture_ops = { 537 .open = snd_pcmtst_pcm_open, 538 .close = snd_pcmtst_pcm_close, 539 .trigger = snd_pcmtst_pcm_trigger, 540 .hw_params = snd_pcmtst_pcm_hw_params, 541 .hw_free = snd_pcmtst_pcm_hw_free, 542 .ioctl = snd_pcmtst_ioctl, 543 .sync_stop = snd_pcmtst_sync_stop, 544 .prepare = snd_pcmtst_pcm_prepare, 545 .pointer = snd_pcmtst_pcm_pointer, 546 }; 547 548 static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst) 549 { 550 struct snd_pcm *pcm; 551 int err; 552 553 err = snd_pcm_new(pcmtst->card, "PCMTest", 0, PLAYBACK_SUBSTREAM_CNT, 554 CAPTURE_SUBSTREAM_CNT, &pcm); 555 if (err < 0) 556 return err; 557 pcm->private_data = pcmtst; 558 strcpy(pcm->name, "PCMTest"); 559 pcmtst->pcm = pcm; 560 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcmtst_playback_ops); 561 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcmtst_capture_ops); 562 563 err = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pcmtst->pdev->dev, 564 0, 128 * 1024); 565 return err; 566 } 567 568 static int snd_pcmtst_create(struct snd_card *card, struct platform_device *pdev, 569 struct pcmtst **r_pcmtst) 570 { 571 struct pcmtst *pcmtst; 572 int err; 573 static const struct snd_device_ops ops = { 574 .dev_free = snd_pcmtst_dev_free, 575 }; 576 577 pcmtst = kzalloc(sizeof(*pcmtst), GFP_KERNEL); 578 if (!pcmtst) 579 return -ENOMEM; 580 pcmtst->card = card; 581 pcmtst->pdev = pdev; 582 583 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pcmtst, &ops); 584 if (err < 0) 585 goto _err_free_chip; 586 587 err = snd_pcmtst_new_pcm(pcmtst); 588 if (err < 0) 589 goto _err_free_chip; 590 591 *r_pcmtst = pcmtst; 592 return 0; 593 594 _err_free_chip: 595 snd_pcmtst_free(pcmtst); 596 return err; 597 } 598 599 static int pcmtst_probe(struct platform_device *pdev) 600 { 601 struct snd_card *card; 602 struct pcmtst *pcmtst; 603 int err; 604 605 err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 606 if (err) 607 return err; 608 609 err = snd_devm_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card); 610 if (err < 0) 611 return err; 612 err = snd_pcmtst_create(card, pdev, &pcmtst); 613 if (err < 0) 614 return err; 615 616 strcpy(card->driver, "PCM-TEST Driver"); 617 strcpy(card->shortname, "PCM-Test"); 618 strcpy(card->longname, "PCM-Test virtual driver"); 619 620 err = snd_card_register(card); 621 if (err < 0) 622 return err; 623 624 platform_set_drvdata(pdev, pcmtst); 625 626 return 0; 627 } 628 629 static void pdev_remove(struct platform_device *pdev) 630 { 631 struct pcmtst *pcmtst = platform_get_drvdata(pdev); 632 633 snd_pcmtst_free(pcmtst); 634 } 635 636 static struct platform_device pcmtst_pdev = { 637 .name = "pcmtest", 638 .dev.release = pcmtst_pdev_release, 639 }; 640 641 static struct platform_driver pcmtst_pdrv = { 642 .probe = pcmtst_probe, 643 .remove_new = pdev_remove, 644 .driver = { 645 .name = "pcmtest", 646 }, 647 }; 648 649 static ssize_t pattern_write(struct file *file, const char __user *u_buff, size_t len, loff_t *off) 650 { 651 struct pattern_buf *patt_buf = file->f_inode->i_private; 652 ssize_t to_write = len; 653 654 if (*off + to_write > MAX_PATTERN_LEN) 655 to_write = MAX_PATTERN_LEN - *off; 656 657 // Crop silently everything over the buffer 658 if (to_write <= 0) 659 return len; 660 661 if (copy_from_user(patt_buf->buf + *off, u_buff, to_write)) 662 return -EFAULT; 663 664 patt_buf->len = *off + to_write; 665 *off += to_write; 666 667 return to_write; 668 } 669 670 static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len, loff_t *off) 671 { 672 struct pattern_buf *patt_buf = file->f_inode->i_private; 673 ssize_t to_read = len; 674 675 if (*off + to_read >= MAX_PATTERN_LEN) 676 to_read = MAX_PATTERN_LEN - *off; 677 if (to_read <= 0) 678 return 0; 679 680 if (copy_to_user(u_buff, patt_buf->buf + *off, to_read)) 681 to_read = 0; 682 else 683 *off += to_read; 684 685 return to_read; 686 } 687 688 static const struct file_operations fill_pattern_fops = { 689 .read = pattern_read, 690 .write = pattern_write, 691 }; 692 693 static int setup_patt_bufs(void) 694 { 695 size_t i; 696 697 for (i = 0; i < ARRAY_SIZE(patt_bufs); i++) { 698 patt_bufs[i].buf = kzalloc(MAX_PATTERN_LEN, GFP_KERNEL); 699 if (!patt_bufs[i].buf) 700 break; 701 strcpy(patt_bufs[i].buf, DEFAULT_PATTERN); 702 patt_bufs[i].len = DEFAULT_PATTERN_LEN; 703 } 704 705 return i; 706 } 707 708 static const char * const pattern_files[] = { "fill_pattern0", "fill_pattern1", 709 "fill_pattern2", "fill_pattern3"}; 710 static int init_debug_files(int buf_count) 711 { 712 size_t i; 713 char len_file_name[32]; 714 715 driver_debug_dir = debugfs_create_dir("pcmtest", NULL); 716 if (IS_ERR(driver_debug_dir)) 717 return PTR_ERR(driver_debug_dir); 718 debugfs_create_u8("pc_test", 0444, driver_debug_dir, &playback_capture_test); 719 debugfs_create_u8("ioctl_test", 0444, driver_debug_dir, &ioctl_reset_test); 720 721 for (i = 0; i < buf_count; i++) { 722 debugfs_create_file(pattern_files[i], 0600, driver_debug_dir, 723 &patt_bufs[i], &fill_pattern_fops); 724 snprintf(len_file_name, sizeof(len_file_name), "%s_len", pattern_files[i]); 725 debugfs_create_u32(len_file_name, 0444, driver_debug_dir, &patt_bufs[i].len); 726 } 727 728 return 0; 729 } 730 731 static void free_pattern_buffers(void) 732 { 733 int i; 734 735 for (i = 0; i < buf_allocated; i++) 736 kfree(patt_bufs[i].buf); 737 } 738 739 static void clear_debug_files(void) 740 { 741 debugfs_remove_recursive(driver_debug_dir); 742 } 743 744 static int __init mod_init(void) 745 { 746 int err = 0; 747 748 buf_allocated = setup_patt_bufs(); 749 if (!buf_allocated) 750 return -ENOMEM; 751 752 snd_pcmtst_hw.channels_max = buf_allocated; 753 754 err = init_debug_files(buf_allocated); 755 if (err) 756 return err; 757 err = platform_device_register(&pcmtst_pdev); 758 if (err) 759 return err; 760 err = platform_driver_register(&pcmtst_pdrv); 761 if (err) 762 platform_device_unregister(&pcmtst_pdev); 763 return err; 764 } 765 766 static void __exit mod_exit(void) 767 { 768 clear_debug_files(); 769 free_pattern_buffers(); 770 771 platform_driver_unregister(&pcmtst_pdrv); 772 platform_device_unregister(&pcmtst_pdev); 773 } 774 775 MODULE_DESCRIPTION("Virtual ALSA driver for PCM testing/fuzzing"); 776 MODULE_LICENSE("GPL"); 777 MODULE_AUTHOR("Ivan Orlov"); 778 module_init(mod_init); 779 module_exit(mod_exit); 780
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.