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