1 // SPDX-License-Identifier: GPL-2.0
3 * Virtual ALSA driver for PCM testing/fuzzing
5 * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com>
7 * This is a simple virtual ALSA driver, which can be used for audio applications/PCM middle layer
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
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.
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.
30 * You can find the corresponding selftest in the 'alsa' selftests folder.
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>
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
51 #define DEFAULT_PATTERN "abacaba"
52 #define DEFAULT_PATTERN_LEN 7
54 #define FILL_MODE_RAND 0
55 #define FILL_MODE_PAT 1
57 #define MAX_PATTERN_LEN 4096
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
;
68 static short fill_mode
= FILL_MODE_PAT
;
70 static u8 playback_capture_test
;
71 static u8 ioctl_reset_test
;
72 static struct dentry
*driver_debug_dir
;
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");
95 struct snd_card
*card
;
96 struct platform_device
*pdev
;
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
;
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
,
126 .channels_max
= MAX_CHANNELS_NUM
,
127 .buffer_bytes_max
= 128 * 1024,
128 .period_bytes_min
= 4096,
129 .period_bytes_max
= 32768,
139 static int buf_allocated
;
140 static struct pattern_buf patt_bufs
[MAX_CHANNELS_NUM
];
142 static inline void inc_buf_pos(struct pcmtst_buf_iter
*v_iter
, size_t by
, size_t bytes
)
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
;
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
155 static inline size_t buf_pos_n(struct pcmtst_buf_iter
*v_iter
, unsigned int channels
,
156 unsigned int chan_num
)
158 return v_iter
->buf_pos
/ channels
+ v_iter
->chan_block
* chan_num
;
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)
166 static inline size_t ch_pos_i(size_t b_total
, unsigned int channels
, unsigned int b_sample
)
168 return b_total
/ channels
/ b_sample
* b_sample
+ (b_total
% b_sample
);
171 static void check_buf_block_i(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
177 for (i
= 0; i
< v_iter
->b_rw
; i
++) {
178 current_byte
= runtime
->dma_area
[v_iter
->buf_pos
];
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
,
184 v_iter
->sample_bytes
)
185 % patt_bufs
[ch_num
].len
]) {
186 v_iter
->is_buf_corrupted
= true;
189 inc_buf_pos(v_iter
, 1, runtime
->dma_bytes
);
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
);
195 static void check_buf_block_ni(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
197 unsigned int channels
= runtime
->channels
;
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
)];
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;
212 inc_buf_pos(v_iter
, 1, runtime
->dma_bytes
);
214 inc_buf_pos(v_iter
, v_iter
->b_rw
- i
, runtime
->dma_bytes
);
218 * Check one block of the buffer. Here we iterate the buffer until we find '0'. 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.
222 static void check_buf_block(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
224 if (v_iter
->interleaved
)
225 check_buf_block_i(v_iter
, runtime
);
227 check_buf_block_ni(v_iter
, runtime
);
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).
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.
238 static void fill_block_pattern_n(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
241 unsigned int channels
= runtime
->channels
;
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
);
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
)
257 size_t pos_in_ch
, pos_pattern
;
258 short ch
, pos_sample
;
260 pos_in_ch
= ch_pos_i(v_iter
->total_bytes
, runtime
->channels
, v_iter
->sample_bytes
);
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
);
274 static void fill_block_pattern(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
276 if (v_iter
->interleaved
)
277 fill_block_pattern_i(v_iter
, runtime
);
279 fill_block_pattern_n(v_iter
, runtime
);
282 static void fill_block_rand_n(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
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
;
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
);
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
);
302 inc_buf_pos(v_iter
, v_iter
->b_rw
, runtime
->dma_bytes
);
305 static void fill_block_rand_i(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
307 size_t in_cur_block
= runtime
->dma_bytes
- v_iter
->buf_pos
;
309 if (v_iter
->b_rw
<= in_cur_block
) {
310 get_random_bytes(&runtime
->dma_area
[v_iter
->buf_pos
], v_iter
->b_rw
);
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
);
315 inc_buf_pos(v_iter
, v_iter
->b_rw
, runtime
->dma_bytes
);
318 static void fill_block_random(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
320 if (v_iter
->interleaved
)
321 fill_block_rand_i(v_iter
, runtime
);
323 fill_block_rand_n(v_iter
, runtime
);
326 static void fill_block(struct pcmtst_buf_iter
*v_iter
, struct snd_pcm_runtime
*runtime
)
330 fill_block_random(v_iter
, runtime
);
333 fill_block_pattern(v_iter
, runtime
);
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.
343 static void timer_timeout(struct timer_list
*data
)
345 struct pcmtst_buf_iter
*v_iter
;
346 struct snd_pcm_substream
*substream
;
348 v_iter
= from_timer(v_iter
, data
, timer_instance
);
349 substream
= v_iter
->substream
;
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
);
359 inc_buf_pos(v_iter
, v_iter
->b_rw
, substream
->runtime
->dma_bytes
);
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
);
367 if (!v_iter
->suspend
)
368 mod_timer(&v_iter
->timer_instance
, jiffies
+ TIMER_INTERVAL
+ inject_delay
);
371 static int snd_pcmtst_pcm_open(struct snd_pcm_substream
*substream
)
373 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
374 struct pcmtst_buf_iter
*v_iter
;
379 v_iter
= kzalloc(sizeof(*v_iter
), GFP_KERNEL
);
383 v_iter
->substream
= substream
;
384 runtime
->hw
= snd_pcmtst_hw
;
385 runtime
->private_data
= v_iter
;
387 playback_capture_test
= 0;
388 ioctl_reset_test
= 0;
390 timer_setup(&v_iter
->timer_instance
, timer_timeout
, 0);
395 static int snd_pcmtst_pcm_close(struct snd_pcm_substream
*substream
)
397 struct pcmtst_buf_iter
*v_iter
= substream
->runtime
->private_data
;
399 timer_shutdown_sync(&v_iter
->timer_instance
);
400 playback_capture_test
= !v_iter
->is_buf_corrupted
;
405 static inline void reset_buf_iterator(struct pcmtst_buf_iter
*v_iter
)
408 v_iter
->is_buf_corrupted
= false;
409 v_iter
->period_pos
= 0;
410 v_iter
->total_bytes
= 0;
413 static inline void start_pcmtest_timer(struct pcmtst_buf_iter
*v_iter
)
415 v_iter
->suspend
= false;
416 mod_timer(&v_iter
->timer_instance
, jiffies
+ TIMER_INTERVAL
);
419 static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
421 struct pcmtst_buf_iter
*v_iter
= substream
->runtime
->private_data
;
423 if (inject_trigger_err
)
426 case SNDRV_PCM_TRIGGER_START
:
427 reset_buf_iterator(v_iter
);
428 start_pcmtest_timer(v_iter
);
430 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
431 start_pcmtest_timer(v_iter
);
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
);
444 static snd_pcm_uframes_t
snd_pcmtst_pcm_pointer(struct snd_pcm_substream
*substream
)
446 struct pcmtst_buf_iter
*v_iter
= substream
->runtime
->private_data
;
448 return bytes_to_frames(substream
->runtime
, v_iter
->buf_pos
);
451 static int snd_pcmtst_free(struct pcmtst
*pcmtst
)
459 // These callbacks are required, but empty - all freeing occurs in pdev_remove
460 static int snd_pcmtst_dev_free(struct snd_device
*device
)
465 static void pcmtst_pdev_release(struct device
*dev
)
469 static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream
*substream
)
471 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
472 struct pcmtst_buf_iter
*v_iter
= runtime
->private_data
;
474 if (inject_prepare_err
)
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;
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
;
492 static int snd_pcmtst_pcm_hw_params(struct snd_pcm_substream
*substream
,
493 struct snd_pcm_hw_params
*params
)
495 if (inject_hwpars_err
)
500 static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream
*substream
)
505 static int snd_pcmtst_ioctl(struct snd_pcm_substream
*substream
, unsigned int cmd
, void *arg
)
508 case SNDRV_PCM_IOCTL1_RESET
:
509 ioctl_reset_test
= 1;
512 return snd_pcm_lib_ioctl(substream
, cmd
, arg
);
515 static int snd_pcmtst_sync_stop(struct snd_pcm_substream
*substream
)
517 struct pcmtst_buf_iter
*v_iter
= substream
->runtime
->private_data
;
519 timer_delete_sync(&v_iter
->timer_instance
);
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
,
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
,
548 static int snd_pcmtst_new_pcm(struct pcmtst
*pcmtst
)
553 err
= snd_pcm_new(pcmtst
->card
, "PCMTest", 0, PLAYBACK_SUBSTREAM_CNT
,
554 CAPTURE_SUBSTREAM_CNT
, &pcm
);
557 pcm
->private_data
= pcmtst
;
558 strcpy(pcm
->name
, "PCMTest");
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
);
563 err
= snd_pcm_set_managed_buffer_all(pcm
, SNDRV_DMA_TYPE_DEV
, &pcmtst
->pdev
->dev
,
568 static int snd_pcmtst_create(struct snd_card
*card
, struct platform_device
*pdev
,
569 struct pcmtst
**r_pcmtst
)
571 struct pcmtst
*pcmtst
;
573 static const struct snd_device_ops ops
= {
574 .dev_free
= snd_pcmtst_dev_free
,
577 pcmtst
= kzalloc(sizeof(*pcmtst
), GFP_KERNEL
);
583 err
= snd_device_new(card
, SNDRV_DEV_LOWLEVEL
, pcmtst
, &ops
);
587 err
= snd_pcmtst_new_pcm(pcmtst
);
595 snd_pcmtst_free(pcmtst
);
599 static int pcmtst_probe(struct platform_device
*pdev
)
601 struct snd_card
*card
;
602 struct pcmtst
*pcmtst
;
605 err
= dma_set_mask_and_coherent(&pdev
->dev
, DMA_BIT_MASK(32));
609 err
= snd_devm_card_new(&pdev
->dev
, index
, id
, THIS_MODULE
, 0, &card
);
612 err
= snd_pcmtst_create(card
, pdev
, &pcmtst
);
616 strcpy(card
->driver
, "PCM-TEST Driver");
617 strcpy(card
->shortname
, "PCM-Test");
618 strcpy(card
->longname
, "PCM-Test virtual driver");
620 err
= snd_card_register(card
);
624 platform_set_drvdata(pdev
, pcmtst
);
629 static void pdev_remove(struct platform_device
*pdev
)
631 struct pcmtst
*pcmtst
= platform_get_drvdata(pdev
);
633 snd_pcmtst_free(pcmtst
);
636 static struct platform_device pcmtst_pdev
= {
638 .dev
.release
= pcmtst_pdev_release
,
641 static struct platform_driver pcmtst_pdrv
= {
642 .probe
= pcmtst_probe
,
643 .remove
= pdev_remove
,
649 static ssize_t
pattern_write(struct file
*file
, const char __user
*u_buff
, size_t len
, loff_t
*off
)
651 struct pattern_buf
*patt_buf
= file
->f_inode
->i_private
;
652 ssize_t to_write
= len
;
654 if (*off
+ to_write
> MAX_PATTERN_LEN
)
655 to_write
= MAX_PATTERN_LEN
- *off
;
657 // Crop silently everything over the buffer
661 if (copy_from_user(patt_buf
->buf
+ *off
, u_buff
, to_write
))
664 patt_buf
->len
= *off
+ to_write
;
670 static ssize_t
pattern_read(struct file
*file
, char __user
*u_buff
, size_t len
, loff_t
*off
)
672 struct pattern_buf
*patt_buf
= file
->f_inode
->i_private
;
673 ssize_t to_read
= len
;
675 if (*off
+ to_read
>= MAX_PATTERN_LEN
)
676 to_read
= MAX_PATTERN_LEN
- *off
;
680 if (copy_to_user(u_buff
, patt_buf
->buf
+ *off
, to_read
))
688 static const struct file_operations fill_pattern_fops
= {
689 .read
= pattern_read
,
690 .write
= pattern_write
,
693 static int setup_patt_bufs(void)
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
)
701 strcpy(patt_bufs
[i
].buf
, DEFAULT_PATTERN
);
702 patt_bufs
[i
].len
= DEFAULT_PATTERN_LEN
;
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
)
713 char len_file_name
[32];
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
);
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
);
731 static void free_pattern_buffers(void)
735 for (i
= 0; i
< buf_allocated
; i
++)
736 kfree(patt_bufs
[i
].buf
);
739 static void clear_debug_files(void)
741 debugfs_remove_recursive(driver_debug_dir
);
744 static int __init
mod_init(void)
748 buf_allocated
= setup_patt_bufs();
752 snd_pcmtst_hw
.channels_max
= buf_allocated
;
754 err
= init_debug_files(buf_allocated
);
757 err
= platform_device_register(&pcmtst_pdev
);
760 err
= platform_driver_register(&pcmtst_pdrv
);
762 platform_device_unregister(&pcmtst_pdev
);
766 static void __exit
mod_exit(void)
769 free_pattern_buffers();
771 platform_driver_unregister(&pcmtst_pdrv
);
772 platform_device_unregister(&pcmtst_pdev
);
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
);