File Index
Files and directories
Sign in
FILE

pcmtest.c

File preview with syntax highlighting.

linux-6.19.14/sound/drivers/pcmtest.c C
  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/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
 60static int index = -1;
 61static char *id = "pcmtest";
 62static bool enable = true;
 63static int inject_delay;
 64static bool inject_hwpars_err;
 65static bool inject_prepare_err;
 66static bool inject_trigger_err;
 67static bool inject_open_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 pcmtest soundcard");
 77module_param(id, charp, 0444);
 78MODULE_PARM_DESC(id, "ID string for pcmtest soundcard");
 79module_param(enable, bool, 0444);
 80MODULE_PARM_DESC(enable, "Enable pcmtest 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");
 91module_param(inject_open_err, bool, 0600);
 92MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback");
 93
 94struct pcmtst {
 95	struct snd_pcm *pcm;
 96	struct snd_card *card;
 97	struct platform_device *pdev;
 98};
 99
100struct 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
116static 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
135struct pattern_buf {
136	char *buf;
137	u32 len;
138};
139
140static int buf_allocated;
141static struct pattern_buf patt_bufs[MAX_CHANNELS_NUM];
142
143static 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 */
156static 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 */
167static 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
172static 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
196static 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 */
223static 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 */
239static 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, ...
255static 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
275static 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
283static 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
306static 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
319static 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
327static 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 */
344static 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
372static 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
396static 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
406static 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
414static 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
420static 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
445static 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
452static 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
461static int snd_pcmtst_dev_free(struct snd_device *device)
462{
463	return 0;
464}
465
466static void pcmtst_pdev_release(struct device *dev)
467{
468}
469
470static 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
493static 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
501static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream *substream)
502{
503	return 0;
504}
505
506static 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
516static 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
525static 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
537static 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
549static 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
569static 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
600static 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
630static void pdev_remove(struct platform_device *pdev)
631{
632	struct pcmtst *pcmtst = platform_get_drvdata(pdev);
633
634	snd_pcmtst_free(pcmtst);
635}
636
637static struct platform_device pcmtst_pdev = {
638	.name =		"pcmtest",
639	.dev.release =	pcmtst_pdev_release,
640};
641
642static struct platform_driver pcmtst_pdrv = {
643	.probe =	pcmtst_probe,
644	.remove =	pdev_remove,
645	.driver =	{
646		.name = "pcmtest",
647	},
648};
649
650static 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
671static 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
689static const struct file_operations fill_pattern_fops = {
690	.read = pattern_read,
691	.write = pattern_write,
692};
693
694static 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 = kmalloc(MAX_PATTERN_LEN, GFP_KERNEL);
700		if (!patt_bufs[i].buf)
701			break;
702		strscpy_pad(patt_bufs[i].buf, DEFAULT_PATTERN, MAX_PATTERN_LEN);
703		patt_bufs[i].len = DEFAULT_PATTERN_LEN;
704	}
705
706	return i;
707}
708
709static const char * const pattern_files[] = { "fill_pattern0", "fill_pattern1",
710					      "fill_pattern2", "fill_pattern3"};
711static 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
732static 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
740static void clear_debug_files(void)
741{
742	debugfs_remove_recursive(driver_debug_dir);
743}
744
745static 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
767static 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
776MODULE_DESCRIPTION("Virtual ALSA driver for PCM testing/fuzzing");
777MODULE_LICENSE("GPL");
778MODULE_AUTHOR("Ivan Orlov");
779module_init(mod_init);
780module_exit(mod_exit);
~/ Read-only file preview.