ALSA: dummy driver - add model parameter

This is a cleanup for the dummy driver. The model kernel module parameter
is introduced to select the soundcard emulation.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2010-02-02 17:48:51 +01:00
parent 6123637faf
commit d5e1ca05f7
1 changed files with 180 additions and 110 deletions

View File

@ -45,109 +45,23 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define MAX_PCM_SUBSTREAMS 128 #define MAX_PCM_SUBSTREAMS 128
#define MAX_MIDI_DEVICES 2 #define MAX_MIDI_DEVICES 2
#if 0 /* emu10k1 emulation */
#define MAX_BUFFER_SIZE (128 * 1024)
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;
}
#define add_playback_constraints emu10k1_playback_constraints
#endif
#if 0 /* RME9652 emulation */
#define MAX_BUFFER_SIZE (26 * 64 * 1024)
#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
#define USE_CHANNELS_MIN 26
#define USE_CHANNELS_MAX 26
#define USE_PERIODS_MIN 2
#define USE_PERIODS_MAX 2
#endif
#if 0 /* ICE1712 emulation */
#define MAX_BUFFER_SIZE (256 * 1024)
#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
#define USE_CHANNELS_MIN 10
#define USE_CHANNELS_MAX 10
#define USE_PERIODS_MIN 1
#define USE_PERIODS_MAX 1024
#endif
#if 0 /* UDA1341 emulation */
#define MAX_BUFFER_SIZE (16380)
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN 2
#define USE_CHANNELS_MAX 2
#define USE_PERIODS_MIN 2
#define USE_PERIODS_MAX 255
#endif
#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN 2
#define USE_CHANNELS_MAX 2
#define USE_RATE SNDRV_PCM_RATE_48000
#define USE_RATE_MIN 48000
#define USE_RATE_MAX 48000
#endif
#if 0 /* CA0106 */
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN 2
#define USE_CHANNELS_MAX 2
#define USE_RATE (SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000)
#define USE_RATE_MIN 48000
#define USE_RATE_MAX 192000
#define MAX_BUFFER_SIZE ((65536-64)*8)
#define MAX_PERIOD_SIZE (65536-64)
#define USE_PERIODS_MIN 2
#define USE_PERIODS_MAX 8
#endif
/* defaults */ /* defaults */
#ifndef MAX_BUFFER_SIZE
#define MAX_BUFFER_SIZE (64*1024) #define MAX_BUFFER_SIZE (64*1024)
#endif #define MIN_PERIOD_SIZE 64
#ifndef MAX_PERIOD_SIZE
#define MAX_PERIOD_SIZE MAX_BUFFER_SIZE #define MAX_PERIOD_SIZE MAX_BUFFER_SIZE
#endif
#ifndef USE_FORMATS
#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) #define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
#endif
#ifndef USE_RATE
#define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000 #define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
#define USE_RATE_MIN 5500 #define USE_RATE_MIN 5500
#define USE_RATE_MAX 48000 #define USE_RATE_MAX 48000
#endif
#ifndef USE_CHANNELS_MIN
#define USE_CHANNELS_MIN 1 #define USE_CHANNELS_MIN 1
#endif
#ifndef USE_CHANNELS_MAX
#define USE_CHANNELS_MAX 2 #define USE_CHANNELS_MAX 2
#endif
#ifndef USE_PERIODS_MIN
#define USE_PERIODS_MIN 1 #define USE_PERIODS_MIN 1
#endif
#ifndef USE_PERIODS_MAX
#define USE_PERIODS_MAX 1024 #define USE_PERIODS_MAX 1024
#endif
#ifndef add_playback_constraints
#define add_playback_constraints(x) 0
#endif
#ifndef add_capture_constraints
#define add_capture_constraints(x) 0
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; static int 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_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
@ -162,6 +76,8 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for dummy soundcard."); MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
module_param_array(enable, bool, NULL, 0444); module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); 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_param_array(pcm_devs, int, NULL, 0444);
MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
module_param_array(pcm_substreams, int, NULL, 0444); module_param_array(pcm_substreams, int, NULL, 0444);
@ -193,15 +109,120 @@ struct dummy_timer_ops {
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
}; };
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_dummy {
struct snd_card *card; struct snd_card *card;
struct dummy_model *model;
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct snd_pcm_hardware pcm_hw;
spinlock_t mixer_lock; spinlock_t mixer_lock;
int mixer_volume[MIXER_ADDR_LAST+1][2]; int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2]; int capture_source[MIXER_ADDR_LAST+1][2];
const struct dummy_timer_ops *timer_ops; const struct dummy_timer_ops *timer_ops;
}; };
/*
* 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;
}
struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
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,
};
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,
};
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,
};
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,
};
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,
};
struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
&model_uda1341,
&model_ac97,
&model_ca0106,
NULL
};
/* /*
* system timer interface * system timer interface
*/ */
@ -509,7 +530,7 @@ static struct snd_pcm_hardware dummy_pcm_hardware = {
.channels_min = USE_CHANNELS_MIN, .channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX, .channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = MAX_BUFFER_SIZE, .buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = 64, .period_bytes_min = MIN_PERIOD_SIZE,
.period_bytes_max = MAX_PERIOD_SIZE, .period_bytes_max = MAX_PERIOD_SIZE,
.periods_min = USE_PERIODS_MIN, .periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX, .periods_max = USE_PERIODS_MAX,
@ -538,6 +559,7 @@ static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
static int dummy_pcm_open(struct snd_pcm_substream *substream) static int dummy_pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_dummy *dummy = snd_pcm_substream_chip(substream); struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
struct dummy_model *model = dummy->model;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
int err; int err;
@ -551,7 +573,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
return err; return err;
runtime->hw = dummy_pcm_hardware; runtime->hw = dummy->pcm_hw;
if (substream->pcm->device & 1) { if (substream->pcm->device & 1) {
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
@ -560,10 +582,16 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID); SNDRV_PCM_INFO_MMAP_VALID);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (model == NULL)
err = add_playback_constraints(substream->runtime); return 0;
else
err = add_capture_constraints(substream->runtime); 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) { if (err < 0) {
dummy->timer_ops->free(substream); dummy->timer_ops->free(substream);
return err; return err;
@ -823,17 +851,19 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
/* /*
* proc interface * proc interface
*/ */
static void print_formats(struct snd_info_buffer *buffer) static void print_formats(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{ {
int i; int i;
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
if (dummy_pcm_hardware.formats & (1ULL << i)) if (dummy->pcm_hw.formats & (1ULL << i))
snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
} }
} }
static void print_rates(struct snd_info_buffer *buffer) static void print_rates(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{ {
static int rates[] = { static int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
@ -841,19 +871,19 @@ static void print_rates(struct snd_info_buffer *buffer)
}; };
int i; int i;
if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS) if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
snd_iprintf(buffer, " continuous"); snd_iprintf(buffer, " continuous");
if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT) if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
snd_iprintf(buffer, " knot"); snd_iprintf(buffer, " knot");
for (i = 0; i < ARRAY_SIZE(rates); i++) for (i = 0; i < ARRAY_SIZE(rates); i++)
if (dummy_pcm_hardware.rates & (1 << i)) if (dummy->pcm_hw.rates & (1 << i))
snd_iprintf(buffer, " %d", rates[i]); snd_iprintf(buffer, " %d", rates[i]);
} }
#define get_dummy_int_ptr(ofs) \ #define get_dummy_int_ptr(dummy, ofs) \
(unsigned int *)((char *)&dummy_pcm_hardware + (ofs)) (unsigned int *)((char *)&((dummy)->pcm_hw) + (ofs))
#define get_dummy_ll_ptr(ofs) \ #define get_dummy_ll_ptr(dummy, ofs) \
(unsigned long long *)((char *)&dummy_pcm_hardware + (ofs)) (unsigned long long *)((char *)&((dummy)->pcm_hw) + (ofs))
struct dummy_hw_field { struct dummy_hw_field {
const char *name; const char *name;
@ -884,20 +914,21 @@ static struct dummy_hw_field fields[] = {
static void dummy_proc_read(struct snd_info_entry *entry, static void dummy_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
struct snd_dummy *dummy = entry->private_data;
int i; int i;
for (i = 0; i < ARRAY_SIZE(fields); i++) { for (i = 0; i < ARRAY_SIZE(fields); i++) {
snd_iprintf(buffer, "%s ", fields[i].name); snd_iprintf(buffer, "%s ", fields[i].name);
if (fields[i].size == sizeof(int)) if (fields[i].size == sizeof(int))
snd_iprintf(buffer, fields[i].format, snd_iprintf(buffer, fields[i].format,
*get_dummy_int_ptr(fields[i].offset)); *get_dummy_int_ptr(dummy, fields[i].offset));
else else
snd_iprintf(buffer, fields[i].format, snd_iprintf(buffer, fields[i].format,
*get_dummy_ll_ptr(fields[i].offset)); *get_dummy_ll_ptr(dummy, fields[i].offset));
if (!strcmp(fields[i].name, "formats")) if (!strcmp(fields[i].name, "formats"))
print_formats(buffer); print_formats(dummy, buffer);
else if (!strcmp(fields[i].name, "rates")) else if (!strcmp(fields[i].name, "rates"))
print_rates(buffer); print_rates(dummy, buffer);
snd_iprintf(buffer, "\n"); snd_iprintf(buffer, "\n");
} }
} }
@ -905,6 +936,7 @@ static void dummy_proc_read(struct snd_info_entry *entry,
static void dummy_proc_write(struct snd_info_entry *entry, static void dummy_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
struct snd_dummy *dummy = entry->private_data;
char line[64]; char line[64];
while (!snd_info_get_line(buffer, line, sizeof(line))) { while (!snd_info_get_line(buffer, line, sizeof(line))) {
@ -924,9 +956,9 @@ static void dummy_proc_write(struct snd_info_entry *entry,
if (strict_strtoull(item, 0, &val)) if (strict_strtoull(item, 0, &val))
continue; continue;
if (fields[i].size == sizeof(int)) if (fields[i].size == sizeof(int))
*get_dummy_int_ptr(fields[i].offset) = val; *get_dummy_int_ptr(dummy, fields[i].offset) = val;
else else
*get_dummy_ll_ptr(fields[i].offset) = val; *get_dummy_ll_ptr(dummy, fields[i].offset) = val;
} }
} }
@ -938,6 +970,7 @@ static void __devinit dummy_proc_init(struct snd_dummy *chip)
snd_info_set_text_ops(entry, chip, dummy_proc_read); snd_info_set_text_ops(entry, chip, dummy_proc_read);
entry->c.text.write = dummy_proc_write; entry->c.text.write = dummy_proc_write;
entry->mode |= S_IWUSR; entry->mode |= S_IWUSR;
entry->private_data = chip;
} }
} }
#else #else
@ -948,6 +981,7 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
{ {
struct snd_card *card; struct snd_card *card;
struct snd_dummy *dummy; struct snd_dummy *dummy;
struct dummy_model *m = NULL, **mdl;
int idx, err; int idx, err;
int dev = devptr->id; int dev = devptr->id;
@ -957,6 +991,15 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
return err; return err;
dummy = card->private_data; dummy = card->private_data;
dummy->card = card; 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++) { for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
if (pcm_substreams[dev] < 1) if (pcm_substreams[dev] < 1)
pcm_substreams[dev] = 1; pcm_substreams[dev] = 1;
@ -966,6 +1009,33 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
if (err < 0) if (err < 0)
goto __nodev; 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); err = snd_card_dummy_new_mixer(dummy);
if (err < 0) if (err < 0)
goto __nodev; goto __nodev;