diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 26 | ||||
-rw-r--r-- | sound/core/control.c | 1 | ||||
-rw-r--r-- | sound/core/info.c | 7 | ||||
-rw-r--r-- | sound/core/info_oss.c | 3 | ||||
-rw-r--r-- | sound/core/oss/mixer_oss.c | 2 | ||||
-rw-r--r-- | sound/core/pcm.c | 4 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 214 | ||||
-rw-r--r-- | sound/core/pcm_memory.c | 26 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 19 | ||||
-rw-r--r-- | sound/core/seq/seq_device.c | 2 | ||||
-rw-r--r-- | sound/core/sgbuf.c | 27 | ||||
-rw-r--r-- | sound/core/sound.c | 3 |
12 files changed, 291 insertions, 43 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index eb60cb8dbb8a..c40ae573346d 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -425,6 +425,26 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, return 0; } +static int snd_compress_check_input(struct snd_compr_params *params) +{ + /* first let's check the buffer parameter's */ + if (params->buffer.fragment_size == 0 || + params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) + return -EINVAL; + + /* now codec parameters */ + if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) + return -EINVAL; + + if (params->codec.ch_in == 0 || params->codec.ch_out == 0) + return -EINVAL; + + if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000)) + return -EINVAL; + + return 0; +} + static int snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) { @@ -443,11 +463,17 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) retval = -EFAULT; goto out; } + + retval = snd_compress_check_input(params); + if (retval) + goto out; + retval = snd_compr_allocate_buffer(stream, params); if (retval) { retval = -ENOMEM; goto out; } + retval = stream->ops->set_params(stream, params); if (retval) goto out; diff --git a/sound/core/control.c b/sound/core/control.c index 2487a6bb1c54..7e86a5b9f3b5 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -246,6 +246,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.count = ncontrol->count ? ncontrol->count : 1; access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| + SNDRV_CTL_ELEM_ACCESS_VOLATILE| SNDRV_CTL_ELEM_ACCESS_INACTIVE| SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| diff --git a/sound/core/info.c b/sound/core/info.c index c1e611c65c8f..6b368d25073b 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -28,7 +28,7 @@ #include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> -#include <sound/version.h> +#include <linux/utsname.h> #include <linux/proc_fs.h> #include <linux/mutex.h> #include <stdarg.h> @@ -986,9 +986,8 @@ static struct snd_info_entry *snd_info_version_entry; static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { snd_iprintf(buffer, - "Advanced Linux Sound Architecture Driver Version " - CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" - ); + "Advanced Linux Sound Architecture Driver Version k%s.\n", + init_utsname()->release); } static int __init snd_info_version_init(void) diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index cf42ab5080eb..83c29dbff9c0 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> -#include <sound/version.h> #include <linux/utsname.h> #include <linux/mutex.h> @@ -94,7 +93,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d static void snd_sndstat_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n"); + snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA emulation code)\n"); snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n", init_utsname()->sysname, init_utsname()->nodename, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 18297f7f2c55..29f6ded02555 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1046,6 +1046,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix if (kctl->info(kctl, uinfo)) { up_read(&mixer->card->controls_rwsem); + kfree(uinfo); return 0; } strcpy(str, ptr->name); @@ -1061,6 +1062,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix uinfo->value.enumerated.item = slot.capture_item; if (kctl->info(kctl, uinfo)) { up_read(&mixer->card->controls_rwsem); + kfree(uinfo); return 0; } if (!strcmp(uinfo->value.enumerated.name, str)) { diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1a3070b4e5b5..f2991940b271 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1105,6 +1105,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) break; } snd_unregister_device(devtype, pcm->card, pcm->device); + if (pcm->streams[cidx].chmap_kctl) { + snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); + pcm->streams[cidx].chmap_kctl = NULL; + } } unlock: mutex_unlock(®ister_mutex); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7ae671923393..f42c10a43315 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -26,6 +26,7 @@ #include <linux/export.h> #include <sound/core.h> #include <sound/control.h> +#include <sound/tlv.h> #include <sound/info.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -2302,3 +2303,216 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, } EXPORT_SYMBOL(snd_pcm_lib_readv); + +/* + * standard channel mapping helpers + */ + +/* default channel maps for multi-channel playbacks, up to 8 channels */ +const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_MONO } }, + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, + { .channels = 8, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, + { } +}; +EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps); + +/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */ +const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_MONO } }, + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 8, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, + { } +}; +EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps); + +static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch) +{ + if (ch > info->max_channels) + return false; + return !info->channel_mask || (info->channel_mask & (1U << ch)); +} + +static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 0; + uinfo->count = info->max_channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +/* get callback for channel map ctl element + * stores the channel position firstly matching with the current channels + */ +static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + const struct snd_pcm_chmap_elem *map; + + if (snd_BUG_ON(!info->chmap)) + return -EINVAL; + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + if (!substream->runtime) + return 0; /* no channels set */ + for (map = info->chmap; map->channels; map++) { + int i; + if (map->channels == substream->runtime->channels && + valid_chmap_channels(info, map->channels)) { + for (i = 0; i < map->channels; i++) + ucontrol->value.integer.value[i] = map->map[i]; + return 0; + } + } + return -EINVAL; +} + +/* tlv callback for channel map ctl element + * expands the pre-defined channel maps in a form of TLV + */ +static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + const struct snd_pcm_chmap_elem *map; + unsigned int __user *dst; + int c, count = 0; + + if (snd_BUG_ON(!info->chmap)) + return -EINVAL; + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (map = info->chmap; map->channels; map++) { + int chs_bytes = map->channels * 4; + if (!valid_chmap_channels(info, map->channels)) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + for (c = 0; c < map->channels; c++) { + if (put_user(map->map[c], dst)) + return -EFAULT; + dst++; + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + info->pcm->streams[info->stream].chmap_kctl = NULL; + kfree(info); +} + +/** + * snd_pcm_add_chmap_ctls - create channel-mapping control elements + * @pcm: the assigned PCM instance + * @stream: stream direction + * @chmap: channel map elements (for query) + * @max_channels: the max number of channels for the stream + * @private_value: the value passed to each kcontrol's private_value field + * @info_ret: store struct snd_pcm_chmap instance if non-NULL + * + * Create channel-mapping control elements assigned to the given PCM stream(s). + * Returns zero if succeed, or a negative error value. + */ +int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, + const struct snd_pcm_chmap_elem *chmap, + int max_channels, + unsigned long private_value, + struct snd_pcm_chmap **info_ret) +{ + struct snd_pcm_chmap *info; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .info = pcm_chmap_ctl_info, + .get = pcm_chmap_ctl_get, + .tlv.c = pcm_chmap_ctl_tlv, + }; + int err; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->pcm = pcm; + info->stream = stream; + info->chmap = chmap; + info->max_channels = max_channels; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + knew.name = "Playback Channel Map"; + else + knew.name = "Capture Channel Map"; + knew.device = pcm->device; + knew.count = pcm->streams[stream].substream_count; + knew.private_value = private_value; + info->kctl = snd_ctl_new1(&knew, info); + if (!info->kctl) { + kfree(info); + return -ENOMEM; + } + info->kctl->private_free = pcm_chmap_ctl_private_free; + err = snd_ctl_add(pcm->card, info->kctl); + if (err < 0) + return err; + pcm->streams[stream].chmap_kctl = info->kctl; + if (info_ret) + *info_ret = info; + return 0; +} +EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 957131366dd9..69e01c4fc32d 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -327,32 +327,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne } EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); - -/* - * compute the max chunk size with continuous pages on sg-buffer - */ -unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, - unsigned int ofs, unsigned int size) -{ - struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream); - unsigned int start, end, pg; - - start = ofs >> PAGE_SHIFT; - end = (ofs + size - 1) >> PAGE_SHIFT; - /* check page continuity */ - pg = sg->table[start].addr >> PAGE_SHIFT; - for (;;) { - start++; - if (start > end) - break; - pg++; - if ((sg->table[start].addr >> PAGE_SHIFT) != pg) - return (start << PAGE_SHIFT) - ofs; - } - /* ok, all on continuous pages */ - return size; -} -EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size); #endif /* CONFIG_SND_DMA_SGBUF */ /** diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 53b5ada8f7c3..5e12e5bacbba 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1563,25 +1563,25 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) /* WARNING: Don't forget to fput back the file */ -static struct file *snd_pcm_file_fd(int fd) +static struct file *snd_pcm_file_fd(int fd, int *fput_needed) { struct file *file; struct inode *inode; unsigned int minor; - file = fget(fd); + file = fget_light(fd, fput_needed); if (!file) return NULL; inode = file->f_path.dentry->d_inode; if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major) { - fput(file); + fput_light(file, *fput_needed); return NULL; } minor = iminor(inode); if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) && !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) { - fput(file); + fput_light(file, *fput_needed); return NULL; } return file; @@ -1597,8 +1597,9 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; struct snd_pcm_group *group; + int fput_needed; - file = snd_pcm_file_fd(fd); + file = snd_pcm_file_fd(fd, &fput_needed); if (!file) return -EBADFD; pcm_file = file->private_data; @@ -1633,7 +1634,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) write_unlock_irq(&snd_pcm_link_rwlock); up_write(&snd_pcm_link_rwsem); _nolock: - fput(file); + fput_light(file, fput_needed); if (res < 0) kfree(group); return res; @@ -3038,7 +3039,7 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file return -EINVAL; area->vm_ops = &snd_pcm_vm_ops_status; area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } @@ -3075,7 +3076,7 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file return -EINVAL; area->vm_ops = &snd_pcm_vm_ops_control; area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } #else /* ! coherent mmap */ @@ -3169,7 +3170,7 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { - area->vm_flags |= VM_RESERVED; + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 5cf8d65ed5ef..60e8fc1b3440 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -569,5 +569,7 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers); EXPORT_SYMBOL(snd_seq_device_new); EXPORT_SYMBOL(snd_seq_device_register_driver); EXPORT_SYMBOL(snd_seq_device_unregister_driver); +#ifdef CONFIG_MODULES EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); +#endif diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index d0f00356fc11..0a418503ec41 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/vmalloc.h> +#include <linux/export.h> #include <sound/memalloc.h> @@ -136,3 +137,29 @@ void *snd_malloc_sgbuf_pages(struct device *device, snd_free_sgbuf_pages(dmab); /* free the table */ return NULL; } + +/* + * compute the max chunk size with continuous pages on sg-buffer + */ +unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab, + unsigned int ofs, unsigned int size) +{ + struct snd_sg_buf *sg = dmab->private_data; + unsigned int start, end, pg; + + start = ofs >> PAGE_SHIFT; + end = (ofs + size - 1) >> PAGE_SHIFT; + /* check page continuity */ + pg = sg->table[start].addr >> PAGE_SHIFT; + for (;;) { + start++; + if (start > end) + break; + pg++; + if ((sg->table[start].addr >> PAGE_SHIFT) != pg) + return (start << PAGE_SHIFT) - ofs; + } + /* ok, all on continuous pages */ + return size; +} +EXPORT_SYMBOL(snd_sgbuf_get_chunk_size); diff --git a/sound/core/sound.c b/sound/core/sound.c index 28f35593a750..643976000ce8 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -27,7 +27,6 @@ #include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> -#include <sound/version.h> #include <sound/control.h> #include <sound/initval.h> #include <linux/kmod.h> @@ -468,7 +467,7 @@ static int __init alsa_sound_init(void) } snd_info_minor_register(); #ifndef MODULE - printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); + printk(KERN_INFO "Advanced Linux Sound Architecture Driver Initialized.\n"); #endif return 0; } |