Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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