Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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);