~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/drivers/pcmtest.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 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
 50 
 51 #define DEFAULT_PATTERN         "abacaba"
 52 #define DEFAULT_PATTERN_LEN     7
 53 
 54 #define FILL_MODE_RAND  0
 55 #define FILL_MODE_PAT   1
 56 
 57 #define MAX_PATTERN_LEN 4096
 58 
 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;
 67 
 68 static short fill_mode = FILL_MODE_PAT;
 69 
 70 static u8 playback_capture_test;
 71 static u8 ioctl_reset_test;
 72 static struct dentry *driver_debug_dir;
 73 
 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");
 92 
 93 struct pcmtst {
 94         struct snd_pcm *pcm;
 95         struct snd_card *card;
 96         struct platform_device *pdev;
 97 };
 98 
 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;
113 };
114 
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,
123         .rate_min =             8000,
124         .rate_max =             48000,
125         .channels_min =         1,
126         .channels_max =         MAX_CHANNELS_NUM,
127         .buffer_bytes_max =     128 * 1024,
128         .period_bytes_min =     4096,
129         .period_bytes_max =     32768,
130         .periods_min =          1,
131         .periods_max =          1024,
132 };
133 
134 struct pattern_buf {
135         char *buf;
136         u32 len;
137 };
138 
139 static int buf_allocated;
140 static struct pattern_buf patt_bufs[MAX_CHANNELS_NUM];
141 
142 static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t bytes)
143 {
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;
148 }
149 
150 /*
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
154  */
155 static inline size_t buf_pos_n(struct pcmtst_buf_iter *v_iter, unsigned int channels,
156                                unsigned int chan_num)
157 {
158         return v_iter->buf_pos / channels + v_iter->chan_block * chan_num;
159 }
160 
161 /*
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)
165  */
166 static inline size_t ch_pos_i(size_t b_total, unsigned int channels, unsigned int b_sample)
167 {
168         return b_total / channels / b_sample * b_sample + (b_total % b_sample);
169 }
170 
171 static void check_buf_block_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
172 {
173         size_t i;
174         short ch_num;
175         u8 current_byte;
176 
177         for (i = 0; i < v_iter->b_rw; i++) {
178                 current_byte = runtime->dma_area[v_iter->buf_pos];
179                 if (!current_byte)
180                         break;
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,
183                                                                    runtime->channels,
184                                                                    v_iter->sample_bytes)
185                                                           % patt_bufs[ch_num].len]) {
186                         v_iter->is_buf_corrupted = true;
187                         break;
188                 }
189                 inc_buf_pos(v_iter, 1, runtime->dma_bytes);
190         }
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);
193 }
194 
195 static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
196 {
197         unsigned int channels = runtime->channels;
198         size_t i;
199         short ch_num;
200         u8 current_byte;
201 
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)];
205                 if (!current_byte)
206                         break;
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;
210                         break;
211                 }
212                 inc_buf_pos(v_iter, 1, runtime->dma_bytes);
213         }
214         inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes);
215 }
216 
217 /*
218  * Check one block of the buffer. Here we iterate the buffer until we find ''. 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.
221  */
222 static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
223 {
224         if (v_iter->interleaved)
225                 check_buf_block_i(v_iter, runtime);
226         else
227                 check_buf_block_ni(v_iter, runtime);
228 }
229 
230 /*
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).
234  *
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.
237  */
238 static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
239 {
240         size_t i;
241         unsigned int channels = runtime->channels;
242         short ch_num;
243 
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);
250         }
251 }
252 
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)
255 {
256         size_t sample;
257         size_t pos_in_ch, pos_pattern;
258         short ch, pos_sample;
259 
260         pos_in_ch = ch_pos_i(v_iter->total_bytes, runtime->channels, v_iter->sample_bytes);
261 
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);
269                         }
270                 }
271         }
272 }
273 
274 static void fill_block_pattern(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
275 {
276         if (v_iter->interleaved)
277                 fill_block_pattern_i(v_iter, runtime);
278         else
279                 fill_block_pattern_n(v_iter, runtime);
280 }
281 
282 static void fill_block_rand_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
283 {
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;
287         unsigned int i;
288 
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);
294                 } else {
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);
300                 }
301         }
302         inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
303 }
304 
305 static void fill_block_rand_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
306 {
307         size_t in_cur_block = runtime->dma_bytes - v_iter->buf_pos;
308 
309         if (v_iter->b_rw <= in_cur_block) {
310                 get_random_bytes(&runtime->dma_area[v_iter->buf_pos], v_iter->b_rw);
311         } else {
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);
314         }
315         inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
316 }
317 
318 static void fill_block_random(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
319 {
320         if (v_iter->interleaved)
321                 fill_block_rand_i(v_iter, runtime);
322         else
323                 fill_block_rand_n(v_iter, runtime);
324 }
325 
326 static void fill_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
327 {
328         switch (fill_mode) {
329         case FILL_MODE_RAND:
330                 fill_block_random(v_iter, runtime);
331                 break;
332         case FILL_MODE_PAT:
333                 fill_block_pattern(v_iter, runtime);
334                 break;
335         }
336 }
337 
338 /*
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.
342  */
343 static void timer_timeout(struct timer_list *data)
344 {
345         struct pcmtst_buf_iter *v_iter;
346         struct snd_pcm_substream *substream;
347 
348         v_iter = from_timer(v_iter, data, timer_instance);
349         substream = v_iter->substream;
350 
351         if (v_iter->suspend)
352                 return;
353 
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);
358         else
359                 inc_buf_pos(v_iter, v_iter->b_rw, substream->runtime->dma_bytes);
360 
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);
365         }
366 
367         if (!v_iter->suspend)
368                 mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
369 }
370 
371 static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
372 {
373         struct snd_pcm_runtime *runtime = substream->runtime;
374         struct pcmtst_buf_iter *v_iter;
375 
376         if (inject_open_err)
377                 return -EBUSY;
378 
379         v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL);
380         if (!v_iter)
381                 return -ENOMEM;
382 
383         v_iter->substream = substream;
384         runtime->hw = snd_pcmtst_hw;
385         runtime->private_data = v_iter;
386 
387         playback_capture_test = 0;
388         ioctl_reset_test = 0;
389 
390         timer_setup(&v_iter->timer_instance, timer_timeout, 0);
391 
392         return 0;
393 }
394 
395 static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream)
396 {
397         struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
398 
399         timer_shutdown_sync(&v_iter->timer_instance);
400         playback_capture_test = !v_iter->is_buf_corrupted;
401         kfree(v_iter);
402         return 0;
403 }
404 
405 static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter)
406 {
407         v_iter->buf_pos = 0;
408         v_iter->is_buf_corrupted = false;
409         v_iter->period_pos = 0;
410         v_iter->total_bytes = 0;
411 }
412 
413 static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter)
414 {
415         v_iter->suspend = false;
416         mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
417 }
418 
419 static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
420 {
421         struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
422 
423         if (inject_trigger_err)
424                 return -EINVAL;
425         switch (cmd) {
426         case SNDRV_PCM_TRIGGER_START:
427                 reset_buf_iterator(v_iter);
428                 start_pcmtest_timer(v_iter);
429                 break;
430         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
431                 start_pcmtest_timer(v_iter);
432                 break;
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);
438                 break;
439         }
440 
441         return 0;
442 }
443 
444 static snd_pcm_uframes_t snd_pcmtst_pcm_pointer(struct snd_pcm_substream *substream)
445 {
446         struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
447 
448         return bytes_to_frames(substream->runtime, v_iter->buf_pos);
449 }
450 
451 static int snd_pcmtst_free(struct pcmtst *pcmtst)
452 {
453         if (!pcmtst)
454                 return 0;
455         kfree(pcmtst);
456         return 0;
457 }
458 
459 // These callbacks are required, but empty - all freeing occurs in pdev_remove
460 static int snd_pcmtst_dev_free(struct snd_device *device)
461 {
462         return 0;
463 }
464 
465 static void pcmtst_pdev_release(struct device *dev)
466 {
467 }
468 
469 static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream)
470 {
471         struct snd_pcm_runtime *runtime = substream->runtime;
472         struct pcmtst_buf_iter *v_iter = runtime->private_data;
473 
474         if (inject_prepare_err)
475                 return -EINVAL;
476 
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;
484         }
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;
488 
489         return 0;
490 }
491 
492 static int snd_pcmtst_pcm_hw_params(struct snd_pcm_substream *substream,
493                                     struct snd_pcm_hw_params *params)
494 {
495         if (inject_hwpars_err)
496                 return -EBUSY;
497         return 0;
498 }
499 
500 static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream *substream)
501 {
502         return 0;
503 }
504 
505 static int snd_pcmtst_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
506 {
507         switch (cmd) {
508         case SNDRV_PCM_IOCTL1_RESET:
509                 ioctl_reset_test = 1;
510                 break;
511         }
512         return snd_pcm_lib_ioctl(substream, cmd, arg);
513 }
514 
515 static int snd_pcmtst_sync_stop(struct snd_pcm_substream *substream)
516 {
517         struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
518 
519         timer_delete_sync(&v_iter->timer_instance);
520 
521         return 0;
522 }
523 
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,
534 };
535 
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,
546 };
547 
548 static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst)
549 {
550         struct snd_pcm *pcm;
551         int err;
552 
553         err = snd_pcm_new(pcmtst->card, "PCMTest", 0, PLAYBACK_SUBSTREAM_CNT,
554                           CAPTURE_SUBSTREAM_CNT, &pcm);
555         if (err < 0)
556                 return err;
557         pcm->private_data = pcmtst;
558         strcpy(pcm->name, "PCMTest");
559         pcmtst->pcm = pcm;
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);
562 
563         err = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pcmtst->pdev->dev,
564                                              0, 128 * 1024);
565         return err;
566 }
567 
568 static int snd_pcmtst_create(struct snd_card *card, struct platform_device *pdev,
569                              struct pcmtst **r_pcmtst)
570 {
571         struct pcmtst *pcmtst;
572         int err;
573         static const struct snd_device_ops ops = {
574                 .dev_free = snd_pcmtst_dev_free,
575         };
576 
577         pcmtst = kzalloc(sizeof(*pcmtst), GFP_KERNEL);
578         if (!pcmtst)
579                 return -ENOMEM;
580         pcmtst->card = card;
581         pcmtst->pdev = pdev;
582 
583         err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pcmtst, &ops);
584         if (err < 0)
585                 goto _err_free_chip;
586 
587         err = snd_pcmtst_new_pcm(pcmtst);
588         if (err < 0)
589                 goto _err_free_chip;
590 
591         *r_pcmtst = pcmtst;
592         return 0;
593 
594 _err_free_chip:
595         snd_pcmtst_free(pcmtst);
596         return err;
597 }
598 
599 static int pcmtst_probe(struct platform_device *pdev)
600 {
601         struct snd_card *card;
602         struct pcmtst *pcmtst;
603         int err;
604 
605         err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
606         if (err)
607                 return err;
608 
609         err = snd_devm_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
610         if (err < 0)
611                 return err;
612         err = snd_pcmtst_create(card, pdev, &pcmtst);
613         if (err < 0)
614                 return err;
615 
616         strcpy(card->driver, "PCM-TEST Driver");
617         strcpy(card->shortname, "PCM-Test");
618         strcpy(card->longname, "PCM-Test virtual driver");
619 
620         err = snd_card_register(card);
621         if (err < 0)
622                 return err;
623 
624         platform_set_drvdata(pdev, pcmtst);
625 
626         return 0;
627 }
628 
629 static void pdev_remove(struct platform_device *pdev)
630 {
631         struct pcmtst *pcmtst = platform_get_drvdata(pdev);
632 
633         snd_pcmtst_free(pcmtst);
634 }
635 
636 static struct platform_device pcmtst_pdev = {
637         .name =         "pcmtest",
638         .dev.release =  pcmtst_pdev_release,
639 };
640 
641 static struct platform_driver pcmtst_pdrv = {
642         .probe =        pcmtst_probe,
643         .remove_new =   pdev_remove,
644         .driver =       {
645                 .name = "pcmtest",
646         },
647 };
648 
649 static ssize_t pattern_write(struct file *file, const char __user *u_buff, size_t len, loff_t *off)
650 {
651         struct pattern_buf *patt_buf = file->f_inode->i_private;
652         ssize_t to_write = len;
653 
654         if (*off + to_write > MAX_PATTERN_LEN)
655                 to_write = MAX_PATTERN_LEN - *off;
656 
657         // Crop silently everything over the buffer
658         if (to_write <= 0)
659                 return len;
660 
661         if (copy_from_user(patt_buf->buf + *off, u_buff, to_write))
662                 return -EFAULT;
663 
664         patt_buf->len = *off + to_write;
665         *off += to_write;
666 
667         return to_write;
668 }
669 
670 static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len, loff_t *off)
671 {
672         struct pattern_buf *patt_buf = file->f_inode->i_private;
673         ssize_t to_read = len;
674 
675         if (*off + to_read >= MAX_PATTERN_LEN)
676                 to_read = MAX_PATTERN_LEN - *off;
677         if (to_read <= 0)
678                 return 0;
679 
680         if (copy_to_user(u_buff, patt_buf->buf + *off, to_read))
681                 to_read = 0;
682         else
683                 *off += to_read;
684 
685         return to_read;
686 }
687 
688 static const struct file_operations fill_pattern_fops = {
689         .read = pattern_read,
690         .write = pattern_write,
691 };
692 
693 static int setup_patt_bufs(void)
694 {
695         size_t i;
696 
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)
700                         break;
701                 strcpy(patt_bufs[i].buf, DEFAULT_PATTERN);
702                 patt_bufs[i].len = DEFAULT_PATTERN_LEN;
703         }
704 
705         return i;
706 }
707 
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)
711 {
712         size_t i;
713         char len_file_name[32];
714 
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);
720 
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);
726         }
727 
728         return 0;
729 }
730 
731 static void free_pattern_buffers(void)
732 {
733         int i;
734 
735         for (i = 0; i < buf_allocated; i++)
736                 kfree(patt_bufs[i].buf);
737 }
738 
739 static void clear_debug_files(void)
740 {
741         debugfs_remove_recursive(driver_debug_dir);
742 }
743 
744 static int __init mod_init(void)
745 {
746         int err = 0;
747 
748         buf_allocated = setup_patt_bufs();
749         if (!buf_allocated)
750                 return -ENOMEM;
751 
752         snd_pcmtst_hw.channels_max = buf_allocated;
753 
754         err = init_debug_files(buf_allocated);
755         if (err)
756                 return err;
757         err = platform_device_register(&pcmtst_pdev);
758         if (err)
759                 return err;
760         err = platform_driver_register(&pcmtst_pdrv);
761         if (err)
762                 platform_device_unregister(&pcmtst_pdev);
763         return err;
764 }
765 
766 static void __exit mod_exit(void)
767 {
768         clear_debug_files();
769         free_pattern_buffers();
770 
771         platform_driver_unregister(&pcmtst_pdrv);
772         platform_device_unregister(&pcmtst_pdev);
773 }
774 
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);
780 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php