cregit-Linux how code gets into the kernel

Release 4.13 sound/drivers/dummy.c

Directory: sound/drivers
/*
 *  Dummy soundcard
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/hrtimer.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/info.h>
#include <sound/initval.h>

MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");


#define MAX_PCM_DEVICES		4

#define MAX_PCM_SUBSTREAMS	128

#define MAX_MIDI_DEVICES	2

/* defaults */

#define MAX_BUFFER_SIZE		(64*1024)

#define MIN_PERIOD_SIZE		64

#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE

#define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)

#define USE_RATE		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000

#define USE_RATE_MIN		5500

#define USE_RATE_MAX		48000

#define USE_CHANNELS_MIN 	1

#define USE_CHANNELS_MAX 	2

#define USE_PERIODS_MIN 	1

#define USE_PERIODS_MAX 	1024


static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	
/* Index 0-MAX */

static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	
/* ID for this card */

static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};

static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};

static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};

static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
#ifdef CONFIG_HIGH_RES_TIMERS

static bool hrtimer = 1;
#endif

static bool fake_buffer = 1;

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Soundcard model.");
module_param_array(pcm_devs, int, NULL, 0444);
MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver.");
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
module_param(fake_buffer, bool, 0444);
MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
#ifdef CONFIG_HIGH_RES_TIMERS
module_param(hrtimer, bool, 0644);
MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
#endif


static struct platform_device *devices[SNDRV_CARDS];


#define MIXER_ADDR_MASTER	0

#define MIXER_ADDR_LINE		1

#define MIXER_ADDR_MIC		2

#define MIXER_ADDR_SYNTH	3

#define MIXER_ADDR_CD		4

#define MIXER_ADDR_LAST		4


struct dummy_timer_ops {
	
int (*create)(struct snd_pcm_substream *);
	
void (*free)(struct snd_pcm_substream *);
	
int (*prepare)(struct snd_pcm_substream *);
	
int (*start)(struct snd_pcm_substream *);
	
int (*stop)(struct snd_pcm_substream *);
	
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
};


#define get_dummy_ops(substream) \
	(*(const struct dummy_timer_ops **)(substream)->runtime->private_data)


struct dummy_model {
	
const char *name;
	
int (*playback_constraints)(struct snd_pcm_runtime *runtime);
	
int (*capture_constraints)(struct snd_pcm_runtime *runtime);
	
u64 formats;
	
size_t buffer_bytes_max;
	
size_t period_bytes_min;
	
size_t period_bytes_max;
	
unsigned int periods_min;
	
unsigned int periods_max;
	
unsigned int rates;
	
unsigned int rate_min;
	
unsigned int rate_max;
	
unsigned int channels_min;
	
unsigned int channels_max;
};


struct snd_dummy {
	
struct snd_card *card;
	
struct dummy_model *model;
	
struct snd_pcm *pcm;
	
struct snd_pcm_hardware pcm_hw;
	
spinlock_t mixer_lock;
	
int mixer_volume[MIXER_ADDR_LAST+1][2];
	
int capture_source[MIXER_ADDR_LAST+1][2];
	
int iobox;
	
struct snd_kcontrol *cd_volume_ctl;
	
struct snd_kcontrol *cd_switch_ctl;
};

/*
 * card models
 */


static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime) { int err; err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX); if (err < 0) return err; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4578.95%250.00%
Takashi Iwai1221.05%250.00%
Total57100.00%4100.00%

static struct dummy_model model_emu10k1 = { .name = "emu10k1", .playback_constraints = emu10k1_playback_constraints, .buffer_bytes_max = 128 * 1024, }; static struct dummy_model model_rme9652 = { .name = "rme9652", .buffer_bytes_max = 26 * 64 * 1024, .formats = SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 26, .channels_max = 26, .periods_min = 2, .periods_max = 2, }; static struct dummy_model model_ice1712 = { .name = "ice1712", .buffer_bytes_max = 256 * 1024, .formats = SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 10, .channels_max = 10, .periods_min = 1, .periods_max = 1024, }; static struct dummy_model model_uda1341 = { .name = "uda1341", .buffer_bytes_max = 16380, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 2, .channels_max = 2, .periods_min = 2, .periods_max = 255, }; static struct dummy_model model_ac97 = { .name = "ac97", .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, }; static struct dummy_model model_ca0106 = { .name = "ca0106", .formats = SNDRV_PCM_FMTBIT_S16_LE, .buffer_bytes_max = ((65536-64)*8), .period_bytes_max = (65536-64), .periods_min = 2, .periods_max = 8, .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000, .rate_min = 48000, .rate_max = 192000, }; static struct dummy_model *dummy_models[] = { &model_emu10k1, &model_rme9652, &model_ice1712, &model_uda1341, &model_ac97, &model_ca0106, NULL }; /* * system timer interface */ struct dummy_systimer_pcm { /* ops must be the first item */ const struct dummy_timer_ops *timer_ops; spinlock_t lock; struct timer_list timer; unsigned long base_time; unsigned int frac_pos; /* fractional sample position (based HZ) */ unsigned int frac_period_rest; unsigned int frac_buffer_size; /* buffer_size * HZ */ unsigned int frac_period_size; /* period_size * HZ */ unsigned int rate; int elapsed; struct snd_pcm_substream *substream; };
static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm) { mod_timer(&dpcm->timer, jiffies + (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai3389.19%266.67%
Roman Kollar410.81%133.33%
Total37100.00%3100.00%


static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm) { unsigned long delta; delta = jiffies - dpcm->base_time; if (!delta) return; dpcm->base_time += delta; delta *= dpcm->rate; dpcm->frac_pos += delta; while (dpcm->frac_pos >= dpcm->frac_buffer_size) dpcm->frac_pos -= dpcm->frac_buffer_size; while (dpcm->frac_period_rest <= delta) { dpcm->elapsed++; dpcm->frac_period_rest += dpcm->frac_period_size; } dpcm->frac_period_rest -= delta; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai94100.00%2100.00%
Total94100.00%2100.00%


static int dummy_systimer_start(struct snd_pcm_substream *substream) { struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; spin_lock(&dpcm->lock); dpcm->base_time = jiffies; dummy_systimer_rearm(dpcm); spin_unlock(&dpcm->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai3363.46%480.00%
Jaroslav Kysela1936.54%120.00%
Total52100.00%5100.00%


static int dummy_systimer_stop(struct snd_pcm_substream *substream) { struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; spin_lock(&dpcm->lock); del_timer(&dpcm->timer); spin_unlock(&dpcm->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai3265.31%480.00%
Jaroslav Kysela1734.69%120.00%
Total49100.00%5100.00%


static int dummy_systimer_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct dummy_systimer_pcm *dpcm = runtime->private_data; dpcm->frac_pos = 0; dpcm->rate = runtime->rate; dpcm->frac_buffer_size = runtime->buffer_size * HZ; dpcm->frac_period_size = runtime->period_size * HZ; dpcm->frac_period_rest = dpcm->frac_period_size; dpcm->elapsed = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4353.75%233.33%
Takashi Iwai3746.25%466.67%
Total80100.00%6100.00%


static void dummy_systimer_callback(unsigned long data) { struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data; unsigned long flags; int elapsed = 0; spin_lock_irqsave(&dpcm->lock, flags); dummy_systimer_update(dpcm); dummy_systimer_rearm(dpcm); elapsed = dpcm->elapsed; dpcm->elapsed = 0; spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) snd_pcm_period_elapsed(dpcm->substream); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai5464.29%675.00%
Jaroslav Kysela3035.71%225.00%
Total84100.00%8100.00%


static snd_pcm_uframes_t dummy_systimer_pointer(struct snd_pcm_substream *substream) { struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; snd_pcm_uframes_t pos; spin_lock(&dpcm->lock); dummy_systimer_update(dpcm); pos = dpcm->frac_pos / HZ; spin_unlock(&dpcm->lock); return pos; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai3561.40%457.14%
Jaroslav Kysela2035.09%228.57%
Ahmet Inan23.51%114.29%
Total57100.00%7100.00%


static int dummy_systimer_create(struct snd_pcm_substream *substream) { struct dummy_systimer_pcm *dpcm; dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); if (!dpcm) return -ENOMEM; substream->runtime->private_data = dpcm; setup_timer(&dpcm->timer, dummy_systimer_callback, (unsigned long) dpcm); spin_lock_init(&dpcm->lock); dpcm->substream = substream; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai6987.34%240.00%
Jaroslav Kysela56.33%240.00%
Roman Kollar56.33%120.00%
Total79100.00%5100.00%


static void dummy_systimer_free(struct snd_pcm_substream *substream) { kfree(substream->runtime->private_data); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai20100.00%1100.00%
Total20100.00%1100.00%

static const struct dummy_timer_ops dummy_systimer_ops = { .create = dummy_systimer_create, .free = dummy_systimer_free, .prepare = dummy_systimer_prepare, .start = dummy_systimer_start, .stop = dummy_systimer_stop, .pointer = dummy_systimer_pointer, }; #ifdef CONFIG_HIGH_RES_TIMERS /* * hrtimer interface */ struct dummy_hrtimer_pcm { /* ops must be the first item */ const struct dummy_timer_ops *timer_ops; ktime_t base_time; ktime_t period_time; atomic_t running; struct hrtimer timer; struct tasklet_struct tasklet; struct snd_pcm_substream *substream; };
static void dummy_hrtimer_pcm_elapsed(unsigned long priv) { struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv; if (atomic_read(&dpcm->running)) snd_pcm_period_elapsed(dpcm->substream); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai39100.00%1100.00%
Total39100.00%1100.00%


static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer) { struct dummy_hrtimer_pcm *dpcm; dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer); if (!atomic_read(&dpcm->running)) return HRTIMER_NORESTART; tasklet_schedule(&dpcm->tasklet); hrtimer_forward_now(timer, dpcm->period_time); return HRTIMER_RESTART; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai63100.00%1100.00%
Total63100.00%1100.00%


static int dummy_hrtimer_start(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer); hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL); atomic_set(&dpcm->running, 1); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai61100.00%1100.00%
Total61100.00%1100.00%


static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; atomic_set(&dpcm->running, 0); hrtimer_cancel(&dpcm->timer); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai43100.00%1100.00%
Total43100.00%1100.00%


static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) { hrtimer_cancel(&dpcm->timer); tasklet_kill(&dpcm->tasklet); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai28100.00%2100.00%
Total28100.00%2100.00%


static snd_pcm_uframes_t dummy_hrtimer_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct dummy_hrtimer_pcm *dpcm = runtime->private_data; u64 delta; u32 pos; delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer), dpcm->base_time); delta = div_u64(delta * runtime->rate + 999999, 1000000); div_u64_rem(delta, runtime->buffer_size, &pos); return pos; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai82100.00%1100.00%
Total82100.00%1100.00%


static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct dummy_hrtimer_pcm *dpcm = runtime->private_data; unsigned int period, rate; long sec; unsigned long nsecs; dummy_hrtimer_sync(dpcm); period = runtime->period_size; rate = runtime->rate; sec = period / rate; period %= rate; nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate); dpcm->period_time = ktime_set(sec, nsecs); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai101100.00%1100.00%
Total101100.00%1100.00%


static int dummy_hrtimer_create(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm; dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); if (!dpcm) return -ENOMEM; substream->runtime->private_data = dpcm; hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); dpcm->timer.function = dummy_hrtimer_callback; dpcm->substream = substream; atomic_set(&dpcm->running, 0); tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed, (unsigned long)dpcm); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai101100.00%1100.00%
Total101100.00%1100.00%


static void dummy_hrtimer_free(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; dummy_hrtimer_sync(dpcm); kfree(dpcm); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai32100.00%1100.00%
Total32100.00%1100.00%

static const struct dummy_timer_ops dummy_hrtimer_ops = { .create = dummy_hrtimer_create, .free = dummy_hrtimer_free, .prepare = dummy_hrtimer_prepare, .start = dummy_hrtimer_start, .stop = dummy_hrtimer_stop, .pointer = dummy_hrtimer_pointer, }; #endif /* CONFIG_HIGH_RES_TIMERS */ /* * PCM interface */
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: return get_dummy_ops(substream)->start(substream); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: return get_dummy_ops(substream)->stop(substream); } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai58100.00%2100.00%
Total58100.00%2100.00%


static int dummy_pcm_prepare(struct snd_pcm_substream *substream) { return get_dummy_ops(substream)->prepare(substream); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai22100.00%2100.00%
Total22100.00%2100.00%


static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) { return get_dummy_ops(substream)->pointer(substream); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai22100.00%2100.00%
Total22100.00%2100.00%

static struct snd_pcm_hardware dummy_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = USE_FORMATS, .rates = USE_RATE, .rate_min = USE_RATE_MIN, .rate_max = USE_RATE_MAX, .channels_min = USE_CHANNELS_MIN, .channels_max = USE_CHANNELS_MAX, .buffer_bytes_max = MAX_BUFFER_SIZE, .period_bytes_min = MIN_PERIOD_SIZE, .period_bytes_max = MAX_PERIOD_SIZE, .periods_min = USE_PERIODS_MIN, .periods_max = USE_PERIODS_MAX, .fifo_size = 0, };
static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { if (fake_buffer) { /* runtime->dma_bytes has to be set manually to allow mmap */ substream->runtime->dma_bytes = params_buffer_bytes(hw_params); return 0; } return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai2654.17%375.00%
Jaroslav Kysela2245.83%125.00%
Total48100.00%4100.00%


static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) { if (fake_buffer) return 0; return snd_pcm_lib_free_pages(substream); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai1354.17%480.00%
Jaroslav Kysela1145.83%120.00%
Total24100.00%5100.00%


static int dummy_pcm_open(struct snd_pcm_substream *substream) { struct snd_dummy *dummy = snd_pcm_substream_chip(substream); struct dummy_model *model = dummy->model; struct snd_pcm_runtime *runtime = substream->runtime; const struct dummy_timer_ops *ops; int err; ops = &dummy_systimer_ops; #ifdef CONFIG_HIGH_RES_TIMERS if (hrtimer) ops = &dummy_hrtimer_ops; #endif err = ops->create(substream); if (err < 0) return err; get_dummy_ops(substream) = ops; runtime->hw = dummy->pcm_hw; if (substream->pcm->device & 1) { runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; } if (substream->pcm->device & 2) runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); if (model == NULL) return 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (model->playback_constraints) err = model->playback_constraints(substream->runtime); } else { if (model->capture_constraints) err = model->capture_constraints(substream->runtime); } if (err < 0) { get_dummy_ops(substream)->free(substream); return err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela13959.91%450.00%
Takashi Iwai9340.09%450.00%
Total232100.00%8100.00%


static int dummy_pcm_close(struct snd_pcm_substream *substream) { get_dummy_ops(substream)->free(substream); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai1250.00%375.00%
Jaroslav Kysela1250.00%125.00%
Total24100.00%4100.00%

/* * dummy buffer handling */ static void *dummy_page[2];
static void free_fake_buffer(void) { if (fake_buffer) { int i; for (i = 0; i < 2; i++) if (dummy_page[i]) { free_page((unsigned long)dummy_page[i]); dummy_page[i] = NULL; } } }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai58100.00%1100.00%
Total58100.00%1100.00%


static int alloc_fake_buffer(void) { int i; if (!fake_buffer) return 0; for (i = 0; i < 2; i++) { dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL); if (!dummy_page[i]) { free_fake_buffer(); return -ENOMEM; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai68100.00%1100.00%
Total68100.00%1100.00%


static int dummy_pcm_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, void __user *dst, unsigned long bytes) { return 0; /* do nothing */ }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai31100.00%2100.00%
Total31100.00%2100.00%


static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *dst, unsigned long bytes) { return 0; /* do nothing */ }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai30100.00%2100.00%
Total30100.00%2100.00%


static int dummy_pcm_silence(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long bytes) { return 0; /* do nothing */ }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai26100.00%2100.00%
Total26100.00%2100.00%


static struct page *dummy_pcm_page(struct snd_pcm_substream *substream, unsigned long offset) { return virt_to_page(dummy_page[substream->stream]); /* the same page */ }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai29100.00%1100.00%
Total29100.00%1100.00%

static struct snd_pcm_ops dummy_pcm_ops = { .open = dummy_pcm_open, .close = dummy_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = dummy_pcm_hw_params, .hw_free = dummy_pcm_hw_free, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, .pointer = dummy_pcm_pointer, }; static struct snd_pcm_ops dummy_pcm_ops_no_buf = { .open = dummy_pcm_open, .close = dummy_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = dummy_pcm_hw_params, .hw_free = dummy_pcm_hw_free, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, .pointer = dummy_pcm_pointer, .copy_user = dummy_pcm_copy, .copy_kernel = dummy_pcm_copy_kernel, .fill_silence = dummy_pcm_silence, .page = dummy_pcm_page, };
static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device, int substreams) { struct snd_pcm *pcm; struct snd_pcm_ops *ops; int err; err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm); if (err < 0) return err; dummy->pcm = pcm; if (fake_buffer) ops = &dummy_pcm_ops_no_buf; else ops = &dummy_pcm_ops; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); pcm->private_data = dummy; pcm->info_flags = 0; strcpy(pcm->name, "Dummy PCM"); if (!fake_buffer) { snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, 64*1024); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela10873.47%350.00%
Takashi Iwai3926.53%350.00%
Total147100.00%6100.00%

/* * mixer interface */ #define DUMMY_VOLUME(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .name = xname, .index = xindex, \ .info = snd_dummy_volume_info, \ .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ .private_value = addr, \ .tlv = { .p = db_scale_dummy } }
static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = -50; uinfo->value.integer.max = 100; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4892.31%266.67%
Takashi Iwai47.69%133.33%
Total52100.00%3100.00%


static int snd_dummy_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; spin_lock_irq(&dummy->mixer_lock); ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; spin_unlock_irq(&dummy->mixer_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8691.49%250.00%
Takashi Iwai88.51%250.00%
Total94100.00%4100.00%


static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; int left, right; left = ucontrol->value.integer.value[0]; if (left < -50) left = -50; if (left > 100) left = 100; right = ucontrol->value.integer.value[1]; if (right < -50) right = -50; if (right > 100) right = 100; spin_lock_irq(&dummy->mixer_lock); change = dummy->mixer_volume[addr][0] != left || dummy->mixer_volume[addr][1] != right; dummy->mixer_volume[addr][0] = left; dummy->mixer_volume[addr][1] = right; spin_unlock_irq(&dummy->mixer_lock); return change; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela17195.53%466.67%
Takashi Iwai84.47%233.33%
Total179100.00%6100.00%

static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0); #define DUMMY_CAPSRC(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_dummy_capsrc_info, \ .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \ .private_value = addr } #define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info
static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; spin_lock_irq(&dummy->mixer_lock); ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; spin_unlock_irq(&dummy->mixer_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8691.49%250.00%
Takashi Iwai88.51%250.00%
Total94100.00%4100.00%


static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; int left, right; left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; spin_lock_irq(&dummy->mixer_lock); change = dummy->capture_source[addr][0] != left && dummy->capture_source[addr][1] != right; dummy->capture_source[addr][0] = left; dummy->capture_source[addr][1] = right; spin_unlock_irq(&dummy->mixer_lock); return change; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela13194.24%250.00%
Takashi Iwai85.76%250.00%
Total139100.00%4100.00%


static int snd_dummy_iobox_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info) { const char *const names[] = { "None", "CD Player" }; return snd_ctl_enum_info(info, 1, 2, names); }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch41100.00%1100.00%
Total41100.00%1100.00%


static int snd_dummy_iobox_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); value->value.enumerated.item[0] = dummy->iobox; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch44100.00%1100.00%
Total44100.00%1100.00%


static int snd_dummy_iobox_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) { struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); int changed; if (value->value.enumerated.item[0] > 1) return -EINVAL; changed = value->value.enumerated.item[0] != dummy->iobox; if (changed) { dummy->iobox = value->value.enumerated.item[0]; if (dummy->iobox) { dummy->cd_volume_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; dummy->cd_switch_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; } else { dummy->cd_volume_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; dummy->cd_switch_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO, &dummy->cd_volume_ctl->id); snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO, &dummy->cd_switch_ctl->id); } return changed; }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch186100.00%1100.00%
Total186100.00%1100.00%

static struct snd_kcontrol_new snd_dummy_controls[] = { DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH), DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE), DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC), DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "External I/O Box", .info = snd_dummy_iobox_info, .get = snd_dummy_iobox_get, .put = snd_dummy_iobox_put, }, };
static int snd_card_dummy_new_mixer(struct snd_dummy *dummy) { struct snd_card *card = dummy->card; struct snd_kcontrol *kcontrol; unsigned int idx; int err; spin_lock_init(&dummy->mixer_lock); strcpy(card->mixername, "Dummy Mixer"); dummy->iobox = 1; for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { kcontrol = snd_ctl_new1(&snd_dummy_controls[idx], dummy); err = snd_ctl_add(card, kcontrol); if (err < 0) return err; if (!strcmp(kcontrol->id.name, "CD Volume")) dummy->cd_volume_ctl = kcontrol; else if (!strcmp(kcontrol->id.name, "CD Capture Switch")) dummy->cd_switch_ctl = kcontrol; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8356.08%571.43%
Clemens Ladisch6141.22%114.29%
Takashi Iwai42.70%114.29%
Total148100.00%7100.00%

#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS) /* * proc interface */
static void print_formats(struct snd_dummy *dummy, struct snd_info_buffer *buffer) { int i; for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (dummy->pcm_hw.formats & (1ULL << i)) snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3050.00%360.00%
Takashi Iwai3050.00%240.00%
Total60100.00%5100.00%


static void print_rates(struct snd_dummy *dummy, struct snd_info_buffer *buffer) { static int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, }; int i; if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS) snd_iprintf(buffer, " continuous"); if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_KNOT) snd_iprintf(buffer, " knot"); for (i = 0; i < ARRAY_SIZE(rates); i++) if (dummy->pcm_hw.rates & (1 << i)) snd_iprintf(buffer, " %d", rates[i]); }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai9977.34%125.00%
Jaroslav Kysela2922.66%375.00%
Total128100.00%4100.00%

#define get_dummy_int_ptr(dummy, ofs) \ (unsigned int *)((char *)&((dummy)->pcm_hw) + (ofs)) #define get_dummy_ll_ptr(dummy, ofs) \ (unsigned long long *)((char *)&((dummy)->pcm_hw) + (ofs)) struct dummy_hw_field { const char *name; const char *format; unsigned int offset; unsigned int size; }; #define FIELD_ENTRY(item, fmt) { \ .name = #item, \ .format = fmt, \ .offset = offsetof(struct snd_pcm_hardware, item), \ .size = sizeof(dummy_pcm_hardware.item) } static struct dummy_hw_field fields[] = { FIELD_ENTRY(formats, "%#llx"), FIELD_ENTRY(rates, "%#x"), FIELD_ENTRY(rate_min, "%d"), FIELD_ENTRY(rate_max, "%d"), FIELD_ENTRY(channels_min, "%d"), FIELD_ENTRY(channels_max, "%d"), FIELD_ENTRY(buffer_bytes_max, "%ld"), FIELD_ENTRY(period_bytes_min, "%ld"), FIELD_ENTRY(period_bytes_max, "%ld"), FIELD_ENTRY(periods_min, "%d"), FIELD_ENTRY(periods_max, "%d"), };
static void dummy_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_dummy *dummy = entry->private_data; int i; for (i = 0; i < ARRAY_SIZE(fields); i++) { snd_iprintf(buffer, "%s ", fields[i].name); if (fields[i].size == sizeof(int)) snd_iprintf(buffer, fields[i].format, *get_dummy_int_ptr(dummy, fields[i].offset)); else snd_iprintf(buffer, fields[i].format, *get_dummy_ll_ptr(dummy, fields[i].offset)); if (!strcmp(fields[i].name, "formats")) print_formats(dummy, buffer); else if (!strcmp(fields[i].name, "rates")) print_rates(dummy, buffer); snd_iprintf(buffer, "\n"); } }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai16090.40%150.00%
Jaroslav Kysela179.60%150.00%
Total177100.00%2100.00%


static void dummy_proc_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_dummy *dummy = entry->private_data; char line[64]; while (!snd_info_get_line(buffer, line, sizeof(line))) { char item[20]; const char *ptr; unsigned long long val; int i; ptr = snd_info_get_str(item, line, sizeof(item)); for (i = 0; i < ARRAY_SIZE(fields); i++) { if (!strcmp(item, fields[i].name)) break; } if (i >= ARRAY_SIZE(fields)) continue; snd_info_get_str(item, ptr, sizeof(item)); if (kstrtoull(item, 0, &val)) continue; if (fields[i].size == sizeof(int)) *get_dummy_int_ptr(dummy, fields[i].offset) = val; else *get_dummy_ll_ptr(dummy, fields[i].offset) = val; } }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai18192.82%133.33%
Jaroslav Kysela136.67%133.33%
Jingoo Han10.51%133.33%
Total195100.00%3100.00%


static void dummy_proc_init(struct snd_dummy *chip) { struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) { snd_info_set_text_ops(entry, chip, dummy_proc_read); entry->c.text.write = dummy_proc_write; entry->mode |= S_IWUSR; entry->private_data = chip; } }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai5890.62%150.00%
Jaroslav Kysela69.38%150.00%
Total64100.00%2100.00%

#else #define dummy_proc_init(x) #endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
static int snd_dummy_probe(struct platform_device *devptr) { struct snd_card *card; struct snd_dummy *dummy; struct dummy_model *m = NULL, **mdl; int idx, err; int dev = devptr->id; err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_dummy), &card); if (err < 0) return err; dummy = card->private_data; dummy->card = card; for (mdl = dummy_models; *mdl && model[dev]; mdl++) { if (strcmp(model[dev], (*mdl)->name) == 0) { printk(KERN_INFO "snd-dummy: Using model '%s' for card %i\n", (*mdl)->name, card->number); m = dummy->model = *mdl; break; } } for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) { if (pcm_substreams[dev] < 1) pcm_substreams[dev] = 1; if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]); if (err < 0) goto __nodev; } dummy->pcm_hw = dummy_pcm_hardware; if (m) { if (m->formats) dummy->pcm_hw.formats = m->formats; if (m->buffer_bytes_max) dummy->pcm_hw.buffer_bytes_max = m->buffer_bytes_max; if (m->period_bytes_min) dummy->pcm_hw.period_bytes_min = m->period_bytes_min; if (m->period_bytes_max) dummy->pcm_hw.period_bytes_max = m->period_bytes_max; if (m->periods_min) dummy->pcm_hw.periods_min = m->periods_min; if (m->periods_max) dummy->pcm_hw.periods_max = m->periods_max; if (m->rates) dummy->pcm_hw.rates = m->rates; if (m->rate_min) dummy->pcm_hw.rate_min = m->rate_min; if (m->rate_max) dummy->pcm_hw.rate_max = m->rate_max; if (m->channels_min) dummy->pcm_hw.channels_min = m->channels_min; if (m->channels_max) dummy->pcm_hw.channels_max = m->channels_max; } err = snd_card_dummy_new_mixer(dummy); if (err < 0) goto __nodev; strcpy(card->driver, "Dummy"); strcpy(card->shortname, "Dummy"); sprintf(card->longname, "Dummy %i", dev + 1); dummy_proc_init(dummy); err = snd_card_register(card); if (err == 0) { platform_set_drvdata(devptr, card); return 0; } __nodev: snd_card_free(card); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela30759.61%342.86%
Takashi Iwai20840.39%457.14%
Total515100.00%7100.00%


static int snd_dummy_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai22100.00%1100.00%
Total22100.00%1100.00%

#ifdef CONFIG_PM_SLEEP
static int snd_dummy_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); struct snd_dummy *dummy = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(dummy->pcm); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai47100.00%2100.00%
Total47100.00%2100.00%


static int snd_dummy_resume(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai31100.00%2100.00%
Total31100.00%2100.00%

static SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume); #define SND_DUMMY_PM_OPS &snd_dummy_pm #else #define SND_DUMMY_PM_OPS NULL #endif #define SND_DUMMY_DRIVER "snd_dummy" static struct platform_driver snd_dummy_driver = { .probe = snd_dummy_probe, .remove = snd_dummy_remove, .driver = { .name = SND_DUMMY_DRIVER, .pm = SND_DUMMY_PM_OPS, }, };
static void snd_dummy_unregister_all(void) { int i; for (i = 0; i < ARRAY_SIZE(devices); ++i) platform_device_unregister(devices[i]); platform_driver_unregister(&snd_dummy_driver); free_fake_buffer(); }

Contributors

PersonTokensPropCommitsCommitProp
Clemens Ladisch4193.18%150.00%
Takashi Iwai36.82%150.00%
Total44100.00%2100.00%


static int __init alsa_card_dummy_init(void) { int i, cards, err; err = platform_driver_register(&snd_dummy_driver); if (err < 0) return err; err = alloc_fake_buffer(); if (err < 0) { platform_driver_unregister(&snd_dummy_driver); return err; } cards = 0; for (i = 0; i < SNDRV_CARDS; i++) { struct platform_device *device; if (! enable[i]) continue; device = platform_device_register_simple(SND_DUMMY_DRIVER, i, NULL, 0); if (IS_ERR(device)) continue; if (!platform_get_drvdata(device)) { platform_device_unregister(device); continue; } devices[i] = device; cards++; } if (!cards) { #ifdef MODULE printk(KERN_ERR "Dummy soundcard not found or device busy\n"); #endif snd_dummy_unregister_all(); return -ENODEV; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai7445.40%333.33%
Jaroslav Kysela6036.81%333.33%
René Herman2213.50%222.22%
Clemens Ladisch74.29%111.11%
Total163100.00%9100.00%


static void __exit alsa_card_dummy_exit(void) { snd_dummy_unregister_all(); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela1083.33%150.00%
Clemens Ladisch216.67%150.00%
Total12100.00%2100.00%

module_init(alsa_card_dummy_init) module_exit(alsa_card_dummy_exit)

Overall Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai294547.95%2544.64%
Jaroslav Kysela273044.45%2035.71%
Clemens Ladisch4266.94%23.57%
René Herman220.36%23.57%
Roman Kollar90.15%11.79%
Rusty Russell40.07%23.57%
Ahmet Inan20.03%11.79%
Julia Lawall20.03%11.79%
Paul Gortmaker10.02%11.79%
Jingoo Han10.02%11.79%
Total6142100.00%56100.00%
Directory: sound/drivers
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.