diff options
| -rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 20 | ||||
| -rw-r--r-- | include/sound/info.h | 4 | ||||
| -rw-r--r-- | include/sound/pcm.h | 2 | ||||
| -rw-r--r-- | sound/core/info.c | 4 | ||||
| -rw-r--r-- | sound/core/oss/mixer_oss.c | 3 | ||||
| -rw-r--r-- | sound/core/oss/pcm_oss.c | 3 | ||||
| -rw-r--r-- | sound/core/pcm.c | 26 | ||||
| -rw-r--r-- | sound/drivers/dummy.c | 700 | ||||
| -rw-r--r-- | sound/usb/usbaudio.c | 4 |
9 files changed, 583 insertions, 183 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 4252697a95d6..ea16e7d184a4 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt | |||
| @@ -513,6 +513,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
| 513 | or input, but you may use this module for any application which | 513 | or input, but you may use this module for any application which |
| 514 | requires a sound card (like RealPlayer). | 514 | requires a sound card (like RealPlayer). |
| 515 | 515 | ||
| 516 | pcm_devs - Number of PCM devices assigned to each card | ||
| 517 | (default = 1, up to 4) | ||
| 518 | pcm_substreams - Number of PCM substreams assigned to each PCM | ||
| 519 | (default = 8, up to 16) | ||
| 520 | hrtimer - Use hrtimer (=1, default) or system timer (=0) | ||
| 521 | fake_buffer - Fake buffer allocations (default = 1) | ||
| 522 | |||
| 523 | When multiple PCM devices are created, snd-dummy gives different | ||
| 524 | behavior to each PCM device: | ||
| 525 | 0 = interleaved with mmap support | ||
| 526 | 1 = non-interleaved with mmap support | ||
| 527 | 2 = interleaved without mmap | ||
| 528 | 3 = non-interleaved without mmap | ||
| 529 | |||
| 530 | As default, snd-dummy drivers doesn't allocate the real buffers | ||
| 531 | but either ignores read/write or mmap a single dummy page to all | ||
| 532 | buffer pages, in order to save the resouces. If your apps need | ||
| 533 | the read/ written buffer data to be consistent, pass fake_buffer=0 | ||
| 534 | option. | ||
| 535 | |||
| 516 | The power-management is supported. | 536 | The power-management is supported. |
| 517 | 537 | ||
| 518 | Module snd-echo3g | 538 | Module snd-echo3g |
diff --git a/include/sound/info.h b/include/sound/info.h index 7c2ee1a21b00..112e8949e1a7 100644 --- a/include/sound/info.h +++ b/include/sound/info.h | |||
| @@ -110,13 +110,13 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer); | |||
| 110 | static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {} | 110 | static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {} |
| 111 | #endif | 111 | #endif |
| 112 | 112 | ||
| 113 | int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \ | 113 | int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) \ |
| 114 | __attribute__ ((format (printf, 2, 3))); | 114 | __attribute__ ((format (printf, 2, 3))); |
| 115 | int snd_info_init(void); | 115 | int snd_info_init(void); |
| 116 | int snd_info_done(void); | 116 | int snd_info_done(void); |
| 117 | 117 | ||
| 118 | int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len); | 118 | int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len); |
| 119 | char *snd_info_get_str(char *dest, char *src, int len); | 119 | const char *snd_info_get_str(char *dest, const char *src, int len); |
| 120 | struct snd_info_entry *snd_info_create_module_entry(struct module *module, | 120 | struct snd_info_entry *snd_info_create_module_entry(struct module *module, |
| 121 | const char *name, | 121 | const char *name, |
| 122 | struct snd_info_entry *parent); | 122 | struct snd_info_entry *parent); |
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 1691c7fe35af..de6d981de5d6 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
| @@ -988,4 +988,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) | |||
| 988 | 988 | ||
| 989 | #define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime) | 989 | #define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime) |
| 990 | 990 | ||
| 991 | const char *snd_pcm_format_name(snd_pcm_format_t format); | ||
| 992 | |||
| 991 | #endif /* __SOUND_PCM_H */ | 993 | #endif /* __SOUND_PCM_H */ |
diff --git a/sound/core/info.c b/sound/core/info.c index 3d1f5137420a..d749a0d394a7 100644 --- a/sound/core/info.c +++ b/sound/core/info.c | |||
| @@ -106,7 +106,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, | |||
| 106 | * | 106 | * |
| 107 | * Returns the size of output string. | 107 | * Returns the size of output string. |
| 108 | */ | 108 | */ |
| 109 | int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...) | 109 | int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) |
| 110 | { | 110 | { |
| 111 | va_list args; | 111 | va_list args; |
| 112 | int len, res; | 112 | int len, res; |
| @@ -725,7 +725,7 @@ EXPORT_SYMBOL(snd_info_get_line); | |||
| 725 | * Returns the updated pointer of the original string so that | 725 | * Returns the updated pointer of the original string so that |
| 726 | * it can be used for the next call. | 726 | * it can be used for the next call. |
| 727 | */ | 727 | */ |
| 728 | char *snd_info_get_str(char *dest, char *src, int len) | 728 | const char *snd_info_get_str(char *dest, const char *src, int len) |
| 729 | { | 729 | { |
| 730 | int c; | 730 | int c; |
| 731 | 731 | ||
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 5dcd8a526970..772423889eb3 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c | |||
| @@ -1154,7 +1154,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, | |||
| 1154 | struct snd_info_buffer *buffer) | 1154 | struct snd_info_buffer *buffer) |
| 1155 | { | 1155 | { |
| 1156 | struct snd_mixer_oss *mixer = entry->private_data; | 1156 | struct snd_mixer_oss *mixer = entry->private_data; |
| 1157 | char line[128], str[32], idxstr[16], *cptr; | 1157 | char line[128], str[32], idxstr[16]; |
| 1158 | const char *cptr; | ||
| 1158 | int ch, idx; | 1159 | int ch, idx; |
| 1159 | struct snd_mixer_oss_assign_table *tbl; | 1160 | struct snd_mixer_oss_assign_table *tbl; |
| 1160 | struct slot *slot; | 1161 | struct slot *slot; |
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index dbe406b82591..d8b2d76125b4 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c | |||
| @@ -2836,7 +2836,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, | |||
| 2836 | struct snd_info_buffer *buffer) | 2836 | struct snd_info_buffer *buffer) |
| 2837 | { | 2837 | { |
| 2838 | struct snd_pcm_str *pstr = entry->private_data; | 2838 | struct snd_pcm_str *pstr = entry->private_data; |
| 2839 | char line[128], str[32], task_name[32], *ptr; | 2839 | char line[128], str[32], task_name[32]; |
| 2840 | const char *ptr; | ||
| 2840 | int idx1; | 2841 | int idx1; |
| 2841 | struct snd_pcm_oss_setup *setup, *setup1, template; | 2842 | struct snd_pcm_oss_setup *setup, *setup1, template; |
| 2842 | 2843 | ||
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 145931a9ff30..0c1440121c22 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c | |||
| @@ -162,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, | |||
| 162 | return -ENOIOCTLCMD; | 162 | return -ENOIOCTLCMD; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | #ifdef CONFIG_SND_VERBOSE_PROCFS | ||
| 166 | |||
| 167 | #define STATE(v) [SNDRV_PCM_STATE_##v] = #v | ||
| 168 | #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v | ||
| 169 | #define READY(v) [SNDRV_PCM_READY_##v] = #v | ||
| 170 | #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v | ||
| 171 | #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v | ||
| 172 | #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v | ||
| 173 | #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v | ||
| 174 | #define START(v) [SNDRV_PCM_START_##v] = #v | ||
| 175 | #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v | 165 | #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v |
| 176 | #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v | ||
| 177 | 166 | ||
| 178 | static char *snd_pcm_format_names[] = { | 167 | static char *snd_pcm_format_names[] = { |
| 179 | FORMAT(S8), | 168 | FORMAT(S8), |
| @@ -216,10 +205,23 @@ static char *snd_pcm_format_names[] = { | |||
| 216 | FORMAT(U18_3BE), | 205 | FORMAT(U18_3BE), |
| 217 | }; | 206 | }; |
| 218 | 207 | ||
| 219 | static const char *snd_pcm_format_name(snd_pcm_format_t format) | 208 | const char *snd_pcm_format_name(snd_pcm_format_t format) |
| 220 | { | 209 | { |
| 221 | return snd_pcm_format_names[format]; | 210 | return snd_pcm_format_names[format]; |
| 222 | } | 211 | } |
| 212 | EXPORT_SYMBOL_GPL(snd_pcm_format_name); | ||
| 213 | |||
| 214 | #ifdef CONFIG_SND_VERBOSE_PROCFS | ||
| 215 | |||
| 216 | #define STATE(v) [SNDRV_PCM_STATE_##v] = #v | ||
| 217 | #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v | ||
| 218 | #define READY(v) [SNDRV_PCM_READY_##v] = #v | ||
| 219 | #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v | ||
| 220 | #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v | ||
| 221 | #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v | ||
| 222 | #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v | ||
| 223 | #define START(v) [SNDRV_PCM_START_##v] = #v | ||
| 224 | #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v | ||
| 223 | 225 | ||
| 224 | static char *snd_pcm_stream_names[] = { | 226 | static char *snd_pcm_stream_names[] = { |
| 225 | STREAM(PLAYBACK), | 227 | STREAM(PLAYBACK), |
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 54239d2e0997..6ba066c41d2e 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c | |||
| @@ -25,12 +25,15 @@ | |||
| 25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| 26 | #include <linux/time.h> | 26 | #include <linux/time.h> |
| 27 | #include <linux/wait.h> | 27 | #include <linux/wait.h> |
| 28 | #include <linux/hrtimer.h> | ||
| 29 | #include <linux/math64.h> | ||
| 28 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
| 29 | #include <sound/core.h> | 31 | #include <sound/core.h> |
| 30 | #include <sound/control.h> | 32 | #include <sound/control.h> |
| 31 | #include <sound/tlv.h> | 33 | #include <sound/tlv.h> |
| 32 | #include <sound/pcm.h> | 34 | #include <sound/pcm.h> |
| 33 | #include <sound/rawmidi.h> | 35 | #include <sound/rawmidi.h> |
| 36 | #include <sound/info.h> | ||
| 34 | #include <sound/initval.h> | 37 | #include <sound/initval.h> |
| 35 | 38 | ||
| 36 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 39 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
| @@ -39,7 +42,7 @@ MODULE_LICENSE("GPL"); | |||
| 39 | MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); | 42 | MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); |
| 40 | 43 | ||
| 41 | #define MAX_PCM_DEVICES 4 | 44 | #define MAX_PCM_DEVICES 4 |
| 42 | #define MAX_PCM_SUBSTREAMS 16 | 45 | #define MAX_PCM_SUBSTREAMS 128 |
| 43 | #define MAX_MIDI_DEVICES 2 | 46 | #define MAX_MIDI_DEVICES 2 |
| 44 | 47 | ||
| 45 | #if 0 /* emu10k1 emulation */ | 48 | #if 0 /* emu10k1 emulation */ |
| @@ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | |||
| 148 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | 151 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
| 149 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | 152 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; |
| 150 | //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | 153 | //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; |
| 154 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
| 155 | static int hrtimer = 1; | ||
| 156 | #endif | ||
| 157 | static int fake_buffer = 1; | ||
| 151 | 158 | ||
| 152 | module_param_array(index, int, NULL, 0444); | 159 | module_param_array(index, int, NULL, 0444); |
| 153 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | 160 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); |
| @@ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444); | |||
| 161 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | 168 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); |
| 162 | //module_param_array(midi_devs, int, NULL, 0444); | 169 | //module_param_array(midi_devs, int, NULL, 0444); |
| 163 | //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); | 170 | //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); |
| 171 | module_param(fake_buffer, bool, 0444); | ||
| 172 | MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations."); | ||
| 173 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
| 174 | module_param(hrtimer, bool, 0644); | ||
| 175 | MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); | ||
| 176 | #endif | ||
| 164 | 177 | ||
| 165 | static struct platform_device *devices[SNDRV_CARDS]; | 178 | static struct platform_device *devices[SNDRV_CARDS]; |
| 166 | 179 | ||
| @@ -171,137 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS]; | |||
| 171 | #define MIXER_ADDR_CD 4 | 184 | #define MIXER_ADDR_CD 4 |
| 172 | #define MIXER_ADDR_LAST 4 | 185 | #define MIXER_ADDR_LAST 4 |
| 173 | 186 | ||
| 187 | struct dummy_timer_ops { | ||
| 188 | int (*create)(struct snd_pcm_substream *); | ||
| 189 | void (*free)(struct snd_pcm_substream *); | ||
| 190 | int (*prepare)(struct snd_pcm_substream *); | ||
| 191 | int (*start)(struct snd_pcm_substream *); | ||
| 192 | int (*stop)(struct snd_pcm_substream *); | ||
| 193 | snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); | ||
| 194 | }; | ||
| 195 | |||
| 174 | struct snd_dummy { | 196 | struct snd_dummy { |
| 175 | struct snd_card *card; | 197 | struct snd_card *card; |
| 176 | struct snd_pcm *pcm; | 198 | struct snd_pcm *pcm; |
| 177 | spinlock_t mixer_lock; | 199 | spinlock_t mixer_lock; |
| 178 | int mixer_volume[MIXER_ADDR_LAST+1][2]; | 200 | int mixer_volume[MIXER_ADDR_LAST+1][2]; |
| 179 | int capture_source[MIXER_ADDR_LAST+1][2]; | 201 | int capture_source[MIXER_ADDR_LAST+1][2]; |
| 202 | const struct dummy_timer_ops *timer_ops; | ||
| 180 | }; | 203 | }; |
| 181 | 204 | ||
| 182 | struct snd_dummy_pcm { | 205 | /* |
| 183 | struct snd_dummy *dummy; | 206 | * system timer interface |
| 207 | */ | ||
| 208 | |||
| 209 | struct dummy_systimer_pcm { | ||
| 184 | spinlock_t lock; | 210 | spinlock_t lock; |
| 185 | struct timer_list timer; | 211 | struct timer_list timer; |
| 186 | unsigned int pcm_buffer_size; | 212 | unsigned long base_time; |
| 187 | unsigned int pcm_period_size; | 213 | unsigned int frac_pos; /* fractional sample position (based HZ) */ |
| 188 | unsigned int pcm_bps; /* bytes per second */ | 214 | unsigned int frac_period_rest; |
| 189 | unsigned int pcm_hz; /* HZ */ | 215 | unsigned int frac_buffer_size; /* buffer_size * HZ */ |
| 190 | unsigned int pcm_irq_pos; /* IRQ position */ | 216 | unsigned int frac_period_size; /* period_size * HZ */ |
| 191 | unsigned int pcm_buf_pos; /* position in buffer */ | 217 | unsigned int rate; |
| 218 | int elapsed; | ||
| 192 | struct snd_pcm_substream *substream; | 219 | struct snd_pcm_substream *substream; |
| 193 | }; | 220 | }; |
| 194 | 221 | ||
| 195 | 222 | static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm) | |
| 196 | static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm) | ||
| 197 | { | 223 | { |
| 198 | dpcm->timer.expires = 1 + jiffies; | 224 | dpcm->timer.expires = jiffies + |
| 225 | (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate; | ||
| 199 | add_timer(&dpcm->timer); | 226 | add_timer(&dpcm->timer); |
| 200 | } | 227 | } |
| 201 | 228 | ||
| 202 | static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) | 229 | static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm) |
| 203 | { | 230 | { |
| 204 | del_timer(&dpcm->timer); | 231 | unsigned long delta; |
| 232 | |||
| 233 | delta = jiffies - dpcm->base_time; | ||
| 234 | if (!delta) | ||
| 235 | return; | ||
| 236 | dpcm->base_time += delta; | ||
| 237 | delta *= dpcm->rate; | ||
| 238 | dpcm->frac_pos += delta; | ||
| 239 | while (dpcm->frac_pos >= dpcm->frac_buffer_size) | ||
| 240 | dpcm->frac_pos -= dpcm->frac_buffer_size; | ||
| 241 | while (dpcm->frac_period_rest <= delta) { | ||
| 242 | dpcm->elapsed++; | ||
| 243 | dpcm->frac_period_rest += dpcm->frac_period_size; | ||
| 244 | } | ||
| 245 | dpcm->frac_period_rest -= delta; | ||
| 205 | } | 246 | } |
| 206 | 247 | ||
| 207 | static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 248 | static int dummy_systimer_start(struct snd_pcm_substream *substream) |
| 208 | { | 249 | { |
| 209 | struct snd_pcm_runtime *runtime = substream->runtime; | 250 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; |
| 210 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 251 | spin_lock(&dpcm->lock); |
| 211 | int err = 0; | 252 | dpcm->base_time = jiffies; |
| 253 | dummy_systimer_rearm(dpcm); | ||
| 254 | spin_unlock(&dpcm->lock); | ||
| 255 | return 0; | ||
| 256 | } | ||
| 212 | 257 | ||
| 258 | static int dummy_systimer_stop(struct snd_pcm_substream *substream) | ||
| 259 | { | ||
| 260 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||
| 213 | spin_lock(&dpcm->lock); | 261 | spin_lock(&dpcm->lock); |
| 214 | switch (cmd) { | 262 | del_timer(&dpcm->timer); |
| 215 | case SNDRV_PCM_TRIGGER_START: | ||
| 216 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 217 | snd_card_dummy_pcm_timer_start(dpcm); | ||
| 218 | break; | ||
| 219 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 220 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 221 | snd_card_dummy_pcm_timer_stop(dpcm); | ||
| 222 | break; | ||
| 223 | default: | ||
| 224 | err = -EINVAL; | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | spin_unlock(&dpcm->lock); | 263 | spin_unlock(&dpcm->lock); |
| 228 | return 0; | 264 | return 0; |
| 229 | } | 265 | } |
| 230 | 266 | ||
| 231 | static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) | 267 | static int dummy_systimer_prepare(struct snd_pcm_substream *substream) |
| 232 | { | 268 | { |
| 233 | struct snd_pcm_runtime *runtime = substream->runtime; | 269 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 234 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 270 | struct dummy_systimer_pcm *dpcm = runtime->private_data; |
| 235 | int bps; | ||
| 236 | |||
| 237 | bps = snd_pcm_format_width(runtime->format) * runtime->rate * | ||
| 238 | runtime->channels / 8; | ||
| 239 | |||
| 240 | if (bps <= 0) | ||
| 241 | return -EINVAL; | ||
| 242 | |||
| 243 | dpcm->pcm_bps = bps; | ||
| 244 | dpcm->pcm_hz = HZ; | ||
| 245 | dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream); | ||
| 246 | dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream); | ||
| 247 | dpcm->pcm_irq_pos = 0; | ||
| 248 | dpcm->pcm_buf_pos = 0; | ||
| 249 | 271 | ||
| 250 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area, | 272 | dpcm->frac_pos = 0; |
| 251 | bytes_to_samples(runtime, runtime->dma_bytes)); | 273 | dpcm->rate = runtime->rate; |
| 274 | dpcm->frac_buffer_size = runtime->buffer_size * HZ; | ||
| 275 | dpcm->frac_period_size = runtime->period_size * HZ; | ||
| 276 | dpcm->frac_period_rest = dpcm->frac_period_size; | ||
| 277 | dpcm->elapsed = 0; | ||
| 252 | 278 | ||
| 253 | return 0; | 279 | return 0; |
| 254 | } | 280 | } |
| 255 | 281 | ||
| 256 | static void snd_card_dummy_pcm_timer_function(unsigned long data) | 282 | static void dummy_systimer_callback(unsigned long data) |
| 257 | { | 283 | { |
| 258 | struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data; | 284 | struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data; |
| 259 | unsigned long flags; | 285 | unsigned long flags; |
| 286 | int elapsed = 0; | ||
| 260 | 287 | ||
| 261 | spin_lock_irqsave(&dpcm->lock, flags); | 288 | spin_lock_irqsave(&dpcm->lock, flags); |
| 262 | dpcm->timer.expires = 1 + jiffies; | 289 | dummy_systimer_update(dpcm); |
| 263 | add_timer(&dpcm->timer); | 290 | dummy_systimer_rearm(dpcm); |
| 264 | dpcm->pcm_irq_pos += dpcm->pcm_bps; | 291 | elapsed = dpcm->elapsed; |
| 265 | dpcm->pcm_buf_pos += dpcm->pcm_bps; | 292 | dpcm->elapsed = 0; |
| 266 | dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz; | 293 | spin_unlock_irqrestore(&dpcm->lock, flags); |
| 267 | if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) { | 294 | if (elapsed) |
| 268 | dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz; | 295 | snd_pcm_period_elapsed(dpcm->substream); |
| 269 | spin_unlock_irqrestore(&dpcm->lock, flags); | 296 | } |
| 297 | |||
| 298 | static snd_pcm_uframes_t | ||
| 299 | dummy_systimer_pointer(struct snd_pcm_substream *substream) | ||
| 300 | { | ||
| 301 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||
| 302 | snd_pcm_uframes_t pos; | ||
| 303 | |||
| 304 | spin_lock(&dpcm->lock); | ||
| 305 | dummy_systimer_update(dpcm); | ||
| 306 | pos = dpcm->frac_pos / HZ; | ||
| 307 | spin_unlock(&dpcm->lock); | ||
| 308 | return pos; | ||
| 309 | } | ||
| 310 | |||
| 311 | static int dummy_systimer_create(struct snd_pcm_substream *substream) | ||
| 312 | { | ||
| 313 | struct dummy_systimer_pcm *dpcm; | ||
| 314 | |||
| 315 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||
| 316 | if (!dpcm) | ||
| 317 | return -ENOMEM; | ||
| 318 | substream->runtime->private_data = dpcm; | ||
| 319 | init_timer(&dpcm->timer); | ||
| 320 | dpcm->timer.data = (unsigned long) dpcm; | ||
| 321 | dpcm->timer.function = dummy_systimer_callback; | ||
| 322 | spin_lock_init(&dpcm->lock); | ||
| 323 | dpcm->substream = substream; | ||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | static void dummy_systimer_free(struct snd_pcm_substream *substream) | ||
| 328 | { | ||
| 329 | kfree(substream->runtime->private_data); | ||
| 330 | } | ||
| 331 | |||
| 332 | static struct dummy_timer_ops dummy_systimer_ops = { | ||
| 333 | .create = dummy_systimer_create, | ||
| 334 | .free = dummy_systimer_free, | ||
| 335 | .prepare = dummy_systimer_prepare, | ||
| 336 | .start = dummy_systimer_start, | ||
| 337 | .stop = dummy_systimer_stop, | ||
| 338 | .pointer = dummy_systimer_pointer, | ||
| 339 | }; | ||
| 340 | |||
| 341 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
| 342 | /* | ||
| 343 | * hrtimer interface | ||
| 344 | */ | ||
| 345 | |||
| 346 | struct dummy_hrtimer_pcm { | ||
| 347 | ktime_t base_time; | ||
| 348 | ktime_t period_time; | ||
| 349 | atomic_t running; | ||
| 350 | struct hrtimer timer; | ||
| 351 | struct tasklet_struct tasklet; | ||
| 352 | struct snd_pcm_substream *substream; | ||
| 353 | }; | ||
| 354 | |||
| 355 | static void dummy_hrtimer_pcm_elapsed(unsigned long priv) | ||
| 356 | { | ||
| 357 | struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv; | ||
| 358 | if (atomic_read(&dpcm->running)) | ||
| 270 | snd_pcm_period_elapsed(dpcm->substream); | 359 | snd_pcm_period_elapsed(dpcm->substream); |
| 271 | } else | ||
| 272 | spin_unlock_irqrestore(&dpcm->lock, flags); | ||
| 273 | } | 360 | } |
| 274 | 361 | ||
| 275 | static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) | 362 | static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer) |
| 363 | { | ||
| 364 | struct dummy_hrtimer_pcm *dpcm; | ||
| 365 | |||
| 366 | dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer); | ||
| 367 | if (!atomic_read(&dpcm->running)) | ||
| 368 | return HRTIMER_NORESTART; | ||
| 369 | tasklet_schedule(&dpcm->tasklet); | ||
| 370 | hrtimer_forward_now(timer, dpcm->period_time); | ||
| 371 | return HRTIMER_RESTART; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int dummy_hrtimer_start(struct snd_pcm_substream *substream) | ||
| 375 | { | ||
| 376 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
| 377 | |||
| 378 | dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer); | ||
| 379 | hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL); | ||
| 380 | atomic_set(&dpcm->running, 1); | ||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | |||
| 384 | static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) | ||
| 385 | { | ||
| 386 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
| 387 | |||
| 388 | atomic_set(&dpcm->running, 0); | ||
| 389 | hrtimer_cancel(&dpcm->timer); | ||
| 390 | return 0; | ||
| 391 | } | ||
| 392 | |||
| 393 | static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) | ||
| 394 | { | ||
| 395 | tasklet_kill(&dpcm->tasklet); | ||
| 396 | } | ||
| 397 | |||
| 398 | static snd_pcm_uframes_t | ||
| 399 | dummy_hrtimer_pointer(struct snd_pcm_substream *substream) | ||
| 276 | { | 400 | { |
| 277 | struct snd_pcm_runtime *runtime = substream->runtime; | 401 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 278 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 402 | struct dummy_hrtimer_pcm *dpcm = runtime->private_data; |
| 403 | u64 delta; | ||
| 404 | u32 pos; | ||
| 405 | |||
| 406 | delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer), | ||
| 407 | dpcm->base_time); | ||
| 408 | delta = div_u64(delta * runtime->rate + 999999, 1000000); | ||
| 409 | div_u64_rem(delta, runtime->buffer_size, &pos); | ||
| 410 | return pos; | ||
| 411 | } | ||
| 279 | 412 | ||
| 280 | return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz); | 413 | static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) |
| 414 | { | ||
| 415 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 416 | struct dummy_hrtimer_pcm *dpcm = runtime->private_data; | ||
| 417 | unsigned int period, rate; | ||
| 418 | long sec; | ||
| 419 | unsigned long nsecs; | ||
| 420 | |||
| 421 | dummy_hrtimer_sync(dpcm); | ||
| 422 | period = runtime->period_size; | ||
| 423 | rate = runtime->rate; | ||
| 424 | sec = period / rate; | ||
| 425 | period %= rate; | ||
| 426 | nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate); | ||
| 427 | dpcm->period_time = ktime_set(sec, nsecs); | ||
| 428 | |||
| 429 | return 0; | ||
| 281 | } | 430 | } |
| 282 | 431 | ||
| 283 | static struct snd_pcm_hardware snd_card_dummy_playback = | 432 | static int dummy_hrtimer_create(struct snd_pcm_substream *substream) |
| 284 | { | 433 | { |
| 285 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 434 | struct dummy_hrtimer_pcm *dpcm; |
| 286 | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | 435 | |
| 287 | .formats = USE_FORMATS, | 436 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); |
| 288 | .rates = USE_RATE, | 437 | if (!dpcm) |
| 289 | .rate_min = USE_RATE_MIN, | 438 | return -ENOMEM; |
| 290 | .rate_max = USE_RATE_MAX, | 439 | substream->runtime->private_data = dpcm; |
| 291 | .channels_min = USE_CHANNELS_MIN, | 440 | hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| 292 | .channels_max = USE_CHANNELS_MAX, | 441 | dpcm->timer.function = dummy_hrtimer_callback; |
| 293 | .buffer_bytes_max = MAX_BUFFER_SIZE, | 442 | dpcm->substream = substream; |
| 294 | .period_bytes_min = 64, | 443 | atomic_set(&dpcm->running, 0); |
| 295 | .period_bytes_max = MAX_PERIOD_SIZE, | 444 | tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed, |
| 296 | .periods_min = USE_PERIODS_MIN, | 445 | (unsigned long)dpcm); |
| 297 | .periods_max = USE_PERIODS_MAX, | 446 | return 0; |
| 298 | .fifo_size = 0, | 447 | } |
| 448 | |||
| 449 | static void dummy_hrtimer_free(struct snd_pcm_substream *substream) | ||
| 450 | { | ||
| 451 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
| 452 | dummy_hrtimer_sync(dpcm); | ||
| 453 | kfree(dpcm); | ||
| 454 | } | ||
| 455 | |||
| 456 | static struct dummy_timer_ops dummy_hrtimer_ops = { | ||
| 457 | .create = dummy_hrtimer_create, | ||
| 458 | .free = dummy_hrtimer_free, | ||
| 459 | .prepare = dummy_hrtimer_prepare, | ||
| 460 | .start = dummy_hrtimer_start, | ||
| 461 | .stop = dummy_hrtimer_stop, | ||
| 462 | .pointer = dummy_hrtimer_pointer, | ||
| 299 | }; | 463 | }; |
| 300 | 464 | ||
| 301 | static struct snd_pcm_hardware snd_card_dummy_capture = | 465 | #endif /* CONFIG_HIGH_RES_TIMERS */ |
| 466 | |||
| 467 | /* | ||
| 468 | * PCM interface | ||
| 469 | */ | ||
| 470 | |||
| 471 | static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 302 | { | 472 | { |
| 303 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 473 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); |
| 304 | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | 474 | |
| 475 | switch (cmd) { | ||
| 476 | case SNDRV_PCM_TRIGGER_START: | ||
| 477 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 478 | return dummy->timer_ops->start(substream); | ||
| 479 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 480 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 481 | return dummy->timer_ops->stop(substream); | ||
| 482 | } | ||
| 483 | return -EINVAL; | ||
| 484 | } | ||
| 485 | |||
| 486 | static int dummy_pcm_prepare(struct snd_pcm_substream *substream) | ||
| 487 | { | ||
| 488 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
| 489 | |||
| 490 | return dummy->timer_ops->prepare(substream); | ||
| 491 | } | ||
| 492 | |||
| 493 | static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) | ||
| 494 | { | ||
| 495 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
| 496 | |||
| 497 | return dummy->timer_ops->pointer(substream); | ||
| 498 | } | ||
| 499 | |||
| 500 | static struct snd_pcm_hardware dummy_pcm_hardware = { | ||
| 501 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 502 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 503 | SNDRV_PCM_INFO_RESUME | | ||
| 504 | SNDRV_PCM_INFO_MMAP_VALID), | ||
| 305 | .formats = USE_FORMATS, | 505 | .formats = USE_FORMATS, |
| 306 | .rates = USE_RATE, | 506 | .rates = USE_RATE, |
| 307 | .rate_min = USE_RATE_MIN, | 507 | .rate_min = USE_RATE_MIN, |
| @@ -316,123 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_capture = | |||
| 316 | .fifo_size = 0, | 516 | .fifo_size = 0, |
| 317 | }; | 517 | }; |
| 318 | 518 | ||
| 319 | static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) | 519 | static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, |
| 320 | { | 520 | struct snd_pcm_hw_params *hw_params) |
| 321 | kfree(runtime->private_data); | ||
| 322 | } | ||
| 323 | |||
| 324 | static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream, | ||
| 325 | struct snd_pcm_hw_params *hw_params) | ||
| 326 | { | 521 | { |
| 327 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | 522 | if (fake_buffer) { |
| 523 | /* runtime->dma_bytes has to be set manually to allow mmap */ | ||
| 524 | substream->runtime->dma_bytes = params_buffer_bytes(hw_params); | ||
| 525 | return 0; | ||
| 526 | } | ||
| 527 | return snd_pcm_lib_malloc_pages(substream, | ||
| 528 | params_buffer_bytes(hw_params)); | ||
| 328 | } | 529 | } |
| 329 | 530 | ||
| 330 | static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) | 531 | static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) |
| 331 | { | 532 | { |
| 533 | if (fake_buffer) | ||
| 534 | return 0; | ||
| 332 | return snd_pcm_lib_free_pages(substream); | 535 | return snd_pcm_lib_free_pages(substream); |
| 333 | } | 536 | } |
| 334 | 537 | ||
| 335 | static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) | 538 | static int dummy_pcm_open(struct snd_pcm_substream *substream) |
| 336 | { | ||
| 337 | struct snd_dummy_pcm *dpcm; | ||
| 338 | |||
| 339 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||
| 340 | if (! dpcm) | ||
| 341 | return dpcm; | ||
| 342 | init_timer(&dpcm->timer); | ||
| 343 | dpcm->timer.data = (unsigned long) dpcm; | ||
| 344 | dpcm->timer.function = snd_card_dummy_pcm_timer_function; | ||
| 345 | spin_lock_init(&dpcm->lock); | ||
| 346 | dpcm->substream = substream; | ||
| 347 | return dpcm; | ||
| 348 | } | ||
| 349 | |||
| 350 | static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream) | ||
| 351 | { | 539 | { |
| 540 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
| 352 | struct snd_pcm_runtime *runtime = substream->runtime; | 541 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 353 | struct snd_dummy_pcm *dpcm; | ||
| 354 | int err; | 542 | int err; |
| 355 | 543 | ||
| 356 | if ((dpcm = new_pcm_stream(substream)) == NULL) | 544 | dummy->timer_ops = &dummy_systimer_ops; |
| 357 | return -ENOMEM; | 545 | #ifdef CONFIG_HIGH_RES_TIMERS |
| 358 | runtime->private_data = dpcm; | 546 | if (hrtimer) |
| 359 | /* makes the infrastructure responsible for freeing dpcm */ | 547 | dummy->timer_ops = &dummy_hrtimer_ops; |
| 360 | runtime->private_free = snd_card_dummy_runtime_free; | 548 | #endif |
| 361 | runtime->hw = snd_card_dummy_playback; | 549 | |
| 550 | err = dummy->timer_ops->create(substream); | ||
| 551 | if (err < 0) | ||
| 552 | return err; | ||
| 553 | |||
| 554 | runtime->hw = dummy_pcm_hardware; | ||
| 362 | if (substream->pcm->device & 1) { | 555 | if (substream->pcm->device & 1) { |
| 363 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | 556 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; |
| 364 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | 557 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; |
| 365 | } | 558 | } |
| 366 | if (substream->pcm->device & 2) | 559 | if (substream->pcm->device & 2) |
| 367 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | 560 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | |
| 368 | err = add_playback_constraints(runtime); | 561 | SNDRV_PCM_INFO_MMAP_VALID); |
| 369 | if (err < 0) | 562 | |
| 563 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 564 | err = add_playback_constraints(substream->runtime); | ||
| 565 | else | ||
| 566 | err = add_capture_constraints(substream->runtime); | ||
| 567 | if (err < 0) { | ||
| 568 | dummy->timer_ops->free(substream); | ||
| 370 | return err; | 569 | return err; |
| 371 | 570 | } | |
| 372 | return 0; | 571 | return 0; |
| 373 | } | 572 | } |
| 374 | 573 | ||
| 375 | static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream) | 574 | static int dummy_pcm_close(struct snd_pcm_substream *substream) |
| 376 | { | 575 | { |
| 377 | struct snd_pcm_runtime *runtime = substream->runtime; | 576 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); |
| 378 | struct snd_dummy_pcm *dpcm; | 577 | dummy->timer_ops->free(substream); |
| 379 | int err; | 578 | return 0; |
| 579 | } | ||
| 380 | 580 | ||
| 381 | if ((dpcm = new_pcm_stream(substream)) == NULL) | 581 | /* |
| 382 | return -ENOMEM; | 582 | * dummy buffer handling |
| 383 | runtime->private_data = dpcm; | 583 | */ |
| 384 | /* makes the infrastructure responsible for freeing dpcm */ | 584 | |
| 385 | runtime->private_free = snd_card_dummy_runtime_free; | 585 | static void *dummy_page[2]; |
| 386 | runtime->hw = snd_card_dummy_capture; | 586 | |
| 387 | if (substream->pcm->device == 1) { | 587 | static void free_fake_buffer(void) |
| 388 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | 588 | { |
| 389 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | 589 | if (fake_buffer) { |
| 590 | int i; | ||
| 591 | for (i = 0; i < 2; i++) | ||
| 592 | if (dummy_page[i]) { | ||
| 593 | free_page((unsigned long)dummy_page[i]); | ||
| 594 | dummy_page[i] = NULL; | ||
| 595 | } | ||
| 390 | } | 596 | } |
| 391 | if (substream->pcm->device & 2) | 597 | } |
| 392 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||
| 393 | err = add_capture_constraints(runtime); | ||
| 394 | if (err < 0) | ||
| 395 | return err; | ||
| 396 | 598 | ||
| 599 | static int alloc_fake_buffer(void) | ||
| 600 | { | ||
| 601 | int i; | ||
| 602 | |||
| 603 | if (!fake_buffer) | ||
| 604 | return 0; | ||
| 605 | for (i = 0; i < 2; i++) { | ||
| 606 | dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL); | ||
| 607 | if (!dummy_page[i]) { | ||
| 608 | free_fake_buffer(); | ||
| 609 | return -ENOMEM; | ||
| 610 | } | ||
| 611 | } | ||
| 397 | return 0; | 612 | return 0; |
| 398 | } | 613 | } |
| 399 | 614 | ||
| 400 | static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream) | 615 | static int dummy_pcm_copy(struct snd_pcm_substream *substream, |
| 616 | int channel, snd_pcm_uframes_t pos, | ||
| 617 | void __user *dst, snd_pcm_uframes_t count) | ||
| 401 | { | 618 | { |
| 402 | return 0; | 619 | return 0; /* do nothing */ |
| 403 | } | 620 | } |
| 404 | 621 | ||
| 405 | static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) | 622 | static int dummy_pcm_silence(struct snd_pcm_substream *substream, |
| 623 | int channel, snd_pcm_uframes_t pos, | ||
| 624 | snd_pcm_uframes_t count) | ||
| 406 | { | 625 | { |
| 407 | return 0; | 626 | return 0; /* do nothing */ |
| 627 | } | ||
| 628 | |||
| 629 | static struct page *dummy_pcm_page(struct snd_pcm_substream *substream, | ||
| 630 | unsigned long offset) | ||
| 631 | { | ||
| 632 | return virt_to_page(dummy_page[substream->stream]); /* the same page */ | ||
| 408 | } | 633 | } |
| 409 | 634 | ||
| 410 | static struct snd_pcm_ops snd_card_dummy_playback_ops = { | 635 | static struct snd_pcm_ops dummy_pcm_ops = { |
| 411 | .open = snd_card_dummy_playback_open, | 636 | .open = dummy_pcm_open, |
| 412 | .close = snd_card_dummy_playback_close, | 637 | .close = dummy_pcm_close, |
| 413 | .ioctl = snd_pcm_lib_ioctl, | 638 | .ioctl = snd_pcm_lib_ioctl, |
| 414 | .hw_params = snd_card_dummy_hw_params, | 639 | .hw_params = dummy_pcm_hw_params, |
| 415 | .hw_free = snd_card_dummy_hw_free, | 640 | .hw_free = dummy_pcm_hw_free, |
| 416 | .prepare = snd_card_dummy_pcm_prepare, | 641 | .prepare = dummy_pcm_prepare, |
| 417 | .trigger = snd_card_dummy_pcm_trigger, | 642 | .trigger = dummy_pcm_trigger, |
| 418 | .pointer = snd_card_dummy_pcm_pointer, | 643 | .pointer = dummy_pcm_pointer, |
| 419 | }; | 644 | }; |
| 420 | 645 | ||
| 421 | static struct snd_pcm_ops snd_card_dummy_capture_ops = { | 646 | static struct snd_pcm_ops dummy_pcm_ops_no_buf = { |
| 422 | .open = snd_card_dummy_capture_open, | 647 | .open = dummy_pcm_open, |
| 423 | .close = snd_card_dummy_capture_close, | 648 | .close = dummy_pcm_close, |
| 424 | .ioctl = snd_pcm_lib_ioctl, | 649 | .ioctl = snd_pcm_lib_ioctl, |
| 425 | .hw_params = snd_card_dummy_hw_params, | 650 | .hw_params = dummy_pcm_hw_params, |
| 426 | .hw_free = snd_card_dummy_hw_free, | 651 | .hw_free = dummy_pcm_hw_free, |
| 427 | .prepare = snd_card_dummy_pcm_prepare, | 652 | .prepare = dummy_pcm_prepare, |
| 428 | .trigger = snd_card_dummy_pcm_trigger, | 653 | .trigger = dummy_pcm_trigger, |
| 429 | .pointer = snd_card_dummy_pcm_pointer, | 654 | .pointer = dummy_pcm_pointer, |
| 655 | .copy = dummy_pcm_copy, | ||
| 656 | .silence = dummy_pcm_silence, | ||
| 657 | .page = dummy_pcm_page, | ||
| 430 | }; | 658 | }; |
| 431 | 659 | ||
| 432 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | 660 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, |
| 433 | int substreams) | 661 | int substreams) |
| 434 | { | 662 | { |
| 435 | struct snd_pcm *pcm; | 663 | struct snd_pcm *pcm; |
| 664 | struct snd_pcm_ops *ops; | ||
| 436 | int err; | 665 | int err; |
| 437 | 666 | ||
| 438 | err = snd_pcm_new(dummy->card, "Dummy PCM", device, | 667 | err = snd_pcm_new(dummy->card, "Dummy PCM", device, |
| @@ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | |||
| 440 | if (err < 0) | 669 | if (err < 0) |
| 441 | return err; | 670 | return err; |
| 442 | dummy->pcm = pcm; | 671 | dummy->pcm = pcm; |
| 443 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); | 672 | if (fake_buffer) |
| 444 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); | 673 | ops = &dummy_pcm_ops_no_buf; |
| 674 | else | ||
| 675 | ops = &dummy_pcm_ops; | ||
| 676 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops); | ||
| 677 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); | ||
| 445 | pcm->private_data = dummy; | 678 | pcm->private_data = dummy; |
| 446 | pcm->info_flags = 0; | 679 | pcm->info_flags = 0; |
| 447 | strcpy(pcm->name, "Dummy PCM"); | 680 | strcpy(pcm->name, "Dummy PCM"); |
| 448 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | 681 | if (!fake_buffer) { |
| 449 | snd_dma_continuous_data(GFP_KERNEL), | 682 | snd_pcm_lib_preallocate_pages_for_all(pcm, |
| 450 | 0, 64*1024); | 683 | SNDRV_DMA_TYPE_CONTINUOUS, |
| 684 | snd_dma_continuous_data(GFP_KERNEL), | ||
| 685 | 0, 64*1024); | ||
| 686 | } | ||
| 451 | return 0; | 687 | return 0; |
| 452 | } | 688 | } |
| 453 | 689 | ||
| 690 | /* | ||
| 691 | * mixer interface | ||
| 692 | */ | ||
| 693 | |||
| 454 | #define DUMMY_VOLUME(xname, xindex, addr) \ | 694 | #define DUMMY_VOLUME(xname, xindex, addr) \ |
| 455 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | 695 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ |
| 456 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ | 696 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ |
| @@ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy) | |||
| 581 | return 0; | 821 | return 0; |
| 582 | } | 822 | } |
| 583 | 823 | ||
| 824 | #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS) | ||
| 825 | /* | ||
| 826 | * proc interface | ||
| 827 | */ | ||
| 828 | static void print_formats(struct snd_info_buffer *buffer) | ||
| 829 | { | ||
| 830 | int i; | ||
| 831 | |||
| 832 | for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { | ||
| 833 | if (dummy_pcm_hardware.formats & (1ULL << i)) | ||
| 834 | snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); | ||
| 835 | } | ||
| 836 | } | ||
| 837 | |||
| 838 | static void print_rates(struct snd_info_buffer *buffer) | ||
| 839 | { | ||
| 840 | static int rates[] = { | ||
| 841 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, | ||
| 842 | 64000, 88200, 96000, 176400, 192000, | ||
| 843 | }; | ||
| 844 | int i; | ||
| 845 | |||
| 846 | if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS) | ||
| 847 | snd_iprintf(buffer, " continuous"); | ||
| 848 | if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT) | ||
| 849 | snd_iprintf(buffer, " knot"); | ||
| 850 | for (i = 0; i < ARRAY_SIZE(rates); i++) | ||
| 851 | if (dummy_pcm_hardware.rates & (1 << i)) | ||
| 852 | snd_iprintf(buffer, " %d", rates[i]); | ||
| 853 | } | ||
| 854 | |||
| 855 | #define get_dummy_int_ptr(ofs) \ | ||
| 856 | (unsigned int *)((char *)&dummy_pcm_hardware + (ofs)) | ||
| 857 | #define get_dummy_ll_ptr(ofs) \ | ||
| 858 | (unsigned long long *)((char *)&dummy_pcm_hardware + (ofs)) | ||
| 859 | |||
| 860 | struct dummy_hw_field { | ||
| 861 | const char *name; | ||
| 862 | const char *format; | ||
| 863 | unsigned int offset; | ||
| 864 | unsigned int size; | ||
| 865 | }; | ||
| 866 | #define FIELD_ENTRY(item, fmt) { \ | ||
| 867 | .name = #item, \ | ||
| 868 | .format = fmt, \ | ||
| 869 | .offset = offsetof(struct snd_pcm_hardware, item), \ | ||
| 870 | .size = sizeof(dummy_pcm_hardware.item) } | ||
| 871 | |||
| 872 | static struct dummy_hw_field fields[] = { | ||
| 873 | FIELD_ENTRY(formats, "%#llx"), | ||
| 874 | FIELD_ENTRY(rates, "%#x"), | ||
| 875 | FIELD_ENTRY(rate_min, "%d"), | ||
| 876 | FIELD_ENTRY(rate_max, "%d"), | ||
| 877 | FIELD_ENTRY(channels_min, "%d"), | ||
| 878 | FIELD_ENTRY(channels_max, "%d"), | ||
| 879 | FIELD_ENTRY(buffer_bytes_max, "%ld"), | ||
| 880 | FIELD_ENTRY(period_bytes_min, "%ld"), | ||
| 881 | FIELD_ENTRY(period_bytes_max, "%ld"), | ||
| 882 | FIELD_ENTRY(periods_min, "%d"), | ||
| 883 | FIELD_ENTRY(periods_max, "%d"), | ||
| 884 | }; | ||
| 885 | |||
| 886 | static void dummy_proc_read(struct snd_info_entry *entry, | ||
| 887 | struct snd_info_buffer *buffer) | ||
| 888 | { | ||
| 889 | int i; | ||
| 890 | |||
| 891 | for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||
| 892 | snd_iprintf(buffer, "%s ", fields[i].name); | ||
| 893 | if (fields[i].size == sizeof(int)) | ||
| 894 | snd_iprintf(buffer, fields[i].format, | ||
| 895 | *get_dummy_int_ptr(fields[i].offset)); | ||
| 896 | else | ||
| 897 | snd_iprintf(buffer, fields[i].format, | ||
| 898 | *get_dummy_ll_ptr(fields[i].offset)); | ||
| 899 | if (!strcmp(fields[i].name, "formats")) | ||
| 900 | print_formats(buffer); | ||
| 901 | else if (!strcmp(fields[i].name, "rates")) | ||
| 902 | print_rates(buffer); | ||
| 903 | snd_iprintf(buffer, "\n"); | ||
| 904 | } | ||
| 905 | } | ||
| 906 | |||
| 907 | static void dummy_proc_write(struct snd_info_entry *entry, | ||
| 908 | struct snd_info_buffer *buffer) | ||
| 909 | { | ||
| 910 | char line[64]; | ||
| 911 | |||
| 912 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
| 913 | char item[20]; | ||
| 914 | const char *ptr; | ||
| 915 | unsigned long long val; | ||
| 916 | int i; | ||
| 917 | |||
| 918 | ptr = snd_info_get_str(item, line, sizeof(item)); | ||
| 919 | for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||
| 920 | if (!strcmp(item, fields[i].name)) | ||
| 921 | break; | ||
| 922 | } | ||
| 923 | if (i >= ARRAY_SIZE(fields)) | ||
| 924 | continue; | ||
| 925 | snd_info_get_str(item, ptr, sizeof(item)); | ||
| 926 | if (strict_strtoull(item, 0, &val)) | ||
| 927 | continue; | ||
| 928 | if (fields[i].size == sizeof(int)) | ||
| 929 | *get_dummy_int_ptr(fields[i].offset) = val; | ||
| 930 | else | ||
| 931 | *get_dummy_ll_ptr(fields[i].offset) = val; | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | static void __devinit dummy_proc_init(struct snd_dummy *chip) | ||
| 936 | { | ||
| 937 | struct snd_info_entry *entry; | ||
| 938 | |||
| 939 | if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) { | ||
| 940 | snd_info_set_text_ops(entry, chip, dummy_proc_read); | ||
| 941 | entry->c.text.write = dummy_proc_write; | ||
| 942 | entry->mode |= S_IWUSR; | ||
| 943 | } | ||
| 944 | } | ||
| 945 | #else | ||
| 946 | #define dummy_proc_init(x) | ||
| 947 | #endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */ | ||
| 948 | |||
| 584 | static int __devinit snd_dummy_probe(struct platform_device *devptr) | 949 | static int __devinit snd_dummy_probe(struct platform_device *devptr) |
| 585 | { | 950 | { |
| 586 | struct snd_card *card; | 951 | struct snd_card *card; |
| @@ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr) | |||
| 610 | strcpy(card->shortname, "Dummy"); | 975 | strcpy(card->shortname, "Dummy"); |
| 611 | sprintf(card->longname, "Dummy %i", dev + 1); | 976 | sprintf(card->longname, "Dummy %i", dev + 1); |
| 612 | 977 | ||
| 978 | dummy_proc_init(dummy); | ||
| 979 | |||
| 613 | snd_card_set_dev(card, &devptr->dev); | 980 | snd_card_set_dev(card, &devptr->dev); |
| 614 | 981 | ||
| 615 | err = snd_card_register(card); | 982 | err = snd_card_register(card); |
| @@ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void) | |||
| 670 | for (i = 0; i < ARRAY_SIZE(devices); ++i) | 1037 | for (i = 0; i < ARRAY_SIZE(devices); ++i) |
| 671 | platform_device_unregister(devices[i]); | 1038 | platform_device_unregister(devices[i]); |
| 672 | platform_driver_unregister(&snd_dummy_driver); | 1039 | platform_driver_unregister(&snd_dummy_driver); |
| 1040 | free_fake_buffer(); | ||
| 673 | } | 1041 | } |
| 674 | 1042 | ||
| 675 | static int __init alsa_card_dummy_init(void) | 1043 | static int __init alsa_card_dummy_init(void) |
| @@ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void) | |||
| 680 | if (err < 0) | 1048 | if (err < 0) |
| 681 | return err; | 1049 | return err; |
| 682 | 1050 | ||
| 1051 | err = alloc_fake_buffer(); | ||
| 1052 | if (err < 0) { | ||
| 1053 | platform_driver_unregister(&snd_dummy_driver); | ||
| 1054 | return err; | ||
| 1055 | } | ||
| 1056 | |||
| 683 | cards = 0; | 1057 | cards = 0; |
| 684 | for (i = 0; i < SNDRV_CARDS; i++) { | 1058 | for (i = 0; i < SNDRV_CARDS; i++) { |
| 685 | struct platform_device *device; | 1059 | struct platform_device *device; |
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 44b9cdc8a83b..3a53c79f48b8 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c | |||
| @@ -2124,8 +2124,8 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s | |||
| 2124 | fp = list_entry(p, struct audioformat, list); | 2124 | fp = list_entry(p, struct audioformat, list); |
| 2125 | snd_iprintf(buffer, " Interface %d\n", fp->iface); | 2125 | snd_iprintf(buffer, " Interface %d\n", fp->iface); |
| 2126 | snd_iprintf(buffer, " Altset %d\n", fp->altsetting); | 2126 | snd_iprintf(buffer, " Altset %d\n", fp->altsetting); |
| 2127 | snd_iprintf(buffer, " Format: %#x (%d bits)\n", | 2127 | snd_iprintf(buffer, " Format: %s\n", |
| 2128 | fp->format, snd_pcm_format_width(fp->format)); | 2128 | snd_pcm_format_name(fp->format)); |
| 2129 | snd_iprintf(buffer, " Channels: %d\n", fp->channels); | 2129 | snd_iprintf(buffer, " Channels: %d\n", fp->channels); |
| 2130 | snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", | 2130 | snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", |
| 2131 | fp->endpoint & USB_ENDPOINT_NUMBER_MASK, | 2131 | fp->endpoint & USB_ENDPOINT_NUMBER_MASK, |
