diff options
Diffstat (limited to 'sound/core')
74 files changed, 6882 insertions, 2957 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 829ca38b595..313f22e9d92 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -1,24 +1,31 @@ # ALSA soundcard-configuration config SND_TIMER tristate - depends on SND config SND_PCM tristate select SND_TIMER - depends on SND + +config SND_DMAENGINE_PCM + tristate config SND_HWDEP tristate - depends on SND config SND_RAWMIDI tristate - depends on SND + +config SND_COMPRESS_OFFLOAD + tristate + +# To be effective this also requires INPUT - users should say: +# select SND_JACK if INPUT=y || INPUT=SND +# to avoid having to force INPUT on. +config SND_JACK + bool config SND_SEQUENCER tristate "Sequencer support" - depends on SND select SND_TIMER help Say Y or M to enable MIDI sequencer and router support. This @@ -43,12 +50,11 @@ config SND_SEQ_DUMMY will be called snd-seq-dummy. config SND_OSSEMUL + select SOUND_OSS_CORE bool - depends on SND config SND_MIXER_OSS tristate "OSS Mixer API" - depends on SND select SND_OSSEMUL help To enable OSS mixer API emulation (/dev/mixer*), say Y here @@ -61,7 +67,6 @@ config SND_MIXER_OSS config SND_PCM_OSS tristate "OSS PCM (digital audio) API" - depends on SND select SND_OSSEMUL select SND_PCM help @@ -84,7 +89,7 @@ config SND_PCM_OSS_PLUGINS config SND_SEQUENCER_OSS bool "OSS Sequencer API" - depends on SND && SND_SEQUENCER + depends on SND_SEQUENCER select SND_OSSEMUL help Say Y here to enable OSS sequencer emulation (both @@ -96,9 +101,29 @@ config SND_SEQUENCER_OSS this will be compiled as a module. The module will be called snd-seq-oss. +config SND_HRTIMER + tristate "HR-timer backend support" + depends on HIGH_RES_TIMERS + select SND_TIMER + help + Say Y here to enable HR-timer backend for ALSA timer. ALSA uses + the hrtimer as a precise timing source. The ALSA sequencer code + also can use this timing source. + + To compile this driver as a module, choose M here: the module + will be called snd-hrtimer. + +config SND_SEQ_HRTIMER_DEFAULT + bool "Use HR-timer as default sequencer timer" + depends on SND_HRTIMER && SND_SEQUENCER + default y + help + Say Y here to use the HR-timer backend as the default sequencer + timer. + config SND_RTCTIMER tristate "RTC Timer support" - depends on SND && RTC + depends on RTC select SND_TIMER help Say Y here to enable RTC timer support for ALSA. ALSA uses @@ -109,9 +134,13 @@ config SND_RTCTIMER To compile this driver as a module, choose M here: the module will be called snd-rtctimer. + Note that this option is exclusive with the new RTC drivers + (CONFIG_RTC_CLASS) since this requires the old API. + config SND_SEQ_RTCTIMER_DEFAULT bool "Use RTC as default sequencer timer" depends on SND_RTCTIMER && SND_SEQUENCER + depends on !SND_SEQ_HRTIMER_DEFAULT default y help Say Y here to use the RTC timer as the default sequencer @@ -123,7 +152,6 @@ config SND_SEQ_RTCTIMER_DEFAULT config SND_DYNAMIC_MINORS bool "Dynamic device file minor numbers" - depends on SND help If you say Y here, the minor numbers of ALSA device files in /dev/snd/ are allocated dynamically. This allows you to have @@ -132,9 +160,17 @@ config SND_DYNAMIC_MINORS If you are unsure about this, say N here. +config SND_MAX_CARDS + int "Max number of sound cards" + range 4 256 + default 32 + depends on SND_DYNAMIC_MINORS + help + Specify the max number of sound cards that can be assigned + on a single machine. + config SND_SUPPORT_OLD_API bool "Support old ALSA API" - depends on SND default y help Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3 @@ -142,7 +178,7 @@ config SND_SUPPORT_OLD_API config SND_VERBOSE_PROCFS bool "Verbose procfs contents" - depends on SND && PROC_FS + depends on PROC_FS default y help Say Y here to include code for verbose procfs contents (provides @@ -151,7 +187,6 @@ config SND_VERBOSE_PROCFS config SND_VERBOSE_PRINTK bool "Verbose printk" - depends on SND help Say Y here to enable verbose log messages. These messages will help to identify source file and position containing @@ -161,16 +196,17 @@ config SND_VERBOSE_PRINTK config SND_DEBUG bool "Debug" - depends on SND help Say Y here to enable ALSA debug code. -config SND_DEBUG_DETECT - bool "Debug detection" +config SND_DEBUG_VERBOSE + bool "More verbose debug" depends on SND_DEBUG help - Say Y here to enable extra-verbose log messages printed when - detecting devices. + Say Y here to enable extra-verbose debugging messages. + + Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages. + So, say Y only if you are ready to be annoyed. config SND_PCM_XRUN_DEBUG bool "Enable PCM ring buffer overrun/underrun debugging" @@ -181,3 +217,15 @@ config SND_PCM_XRUN_DEBUG It is usually not required, but if you have trouble with sound clicking when system is loaded, it may help to determine the process or driver which causes the scheduling gaps. + +config SND_VMASTER + bool + +config SND_KCTL_JACK + bool + +config SND_DMA_SGBUF + def_bool y + depends on X86 + +source "sound/core/seq/Kconfig" diff --git a/sound/core/Makefile b/sound/core/Makefile index 267039a97bd..394a38909f6 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -6,24 +6,34 @@ snd-y := sound.o init.o memory.o info.o control.o misc.o device.o snd-$(CONFIG_ISA_DMA_API) += isadma.o snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o +snd-$(CONFIG_SND_VMASTER) += vmaster.o +snd-$(CONFIG_SND_KCTL_JACK) += ctljack.o +snd-$(CONFIG_SND_JACK) += jack.o -snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ - pcm_memory.o +snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ + pcm_memory.o memalloc.o +snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o -snd-page-alloc-y := memalloc.o -snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o +snd-pcm-dmaengine-objs := pcm_dmaengine.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o +snd-hrtimer-objs := hrtimer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o +snd-compress-objs := compress_offload.o + obj-$(CONFIG_SND) += snd.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o obj-$(CONFIG_SND_TIMER) += snd-timer.o +obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o -obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_PCM) += snd-pcm.o +obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ obj-$(CONFIG_SND_SEQUENCER) += seq/ + +obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c new file mode 100644 index 00000000000..7403f348ed1 --- /dev/null +++ b/sound/core/compress_offload.c @@ -0,0 +1,998 @@ +/* + * compress_core.c - compress offload core + * + * Copyright (C) 2011 Intel Corporation + * Authors: Vinod Koul <vinod.koul@linux.intel.com> + * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) + +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/math64.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/uio.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/compress_params.h> +#include <sound/compress_offload.h> +#include <sound/compress_driver.h> + +/* TODO: + * - add substream support for multiple devices in case of + * SND_DYNAMIC_MINORS is not used + * - Multiple node representation + * driver should be able to register multiple nodes + */ + +static DEFINE_MUTEX(device_mutex); + +struct snd_compr_file { + unsigned long caps; + struct snd_compr_stream stream; +}; + +/* + * a note on stream states used: + * we use follwing states in the compressed core + * SNDRV_PCM_STATE_OPEN: When stream has been opened. + * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by + * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this + * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. + * SNDRV_PCM_STATE_RUNNING: When stream has been started and is + * decoding/encoding and rendering/capturing data. + * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done + * by calling SNDRV_COMPRESS_DRAIN. + * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling + * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling + * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. + */ +static int snd_compr_open(struct inode *inode, struct file *f) +{ + struct snd_compr *compr; + struct snd_compr_file *data; + struct snd_compr_runtime *runtime; + enum snd_compr_direction dirn; + int maj = imajor(inode); + int ret; + + if ((f->f_flags & O_ACCMODE) == O_WRONLY) + dirn = SND_COMPRESS_PLAYBACK; + else if ((f->f_flags & O_ACCMODE) == O_RDONLY) + dirn = SND_COMPRESS_CAPTURE; + else + return -EINVAL; + + if (maj == snd_major) + compr = snd_lookup_minor_data(iminor(inode), + SNDRV_DEVICE_TYPE_COMPRESS); + else + return -EBADFD; + + if (compr == NULL) { + pr_err("no device data!!!\n"); + return -ENODEV; + } + + if (dirn != compr->direction) { + pr_err("this device doesn't support this direction\n"); + snd_card_unref(compr->card); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + snd_card_unref(compr->card); + return -ENOMEM; + } + data->stream.ops = compr->ops; + data->stream.direction = dirn; + data->stream.private_data = compr->private_data; + data->stream.device = compr; + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) { + kfree(data); + snd_card_unref(compr->card); + return -ENOMEM; + } + runtime->state = SNDRV_PCM_STATE_OPEN; + init_waitqueue_head(&runtime->sleep); + data->stream.runtime = runtime; + f->private_data = (void *)data; + mutex_lock(&compr->lock); + ret = compr->ops->open(&data->stream); + mutex_unlock(&compr->lock); + if (ret) { + kfree(runtime); + kfree(data); + } + snd_card_unref(compr->card); + return ret; +} + +static int snd_compr_free(struct inode *inode, struct file *f) +{ + struct snd_compr_file *data = f->private_data; + struct snd_compr_runtime *runtime = data->stream.runtime; + + switch (runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); + break; + default: + break; + } + + data->stream.ops->free(&data->stream); + kfree(data->stream.runtime->buffer); + kfree(data->stream.runtime); + kfree(data); + return 0; +} + +static int snd_compr_update_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + if (!stream->ops->pointer) + return -ENOTSUPP; + stream->ops->pointer(stream, tstamp); + pr_debug("dsp consumed till %d total %d bytes\n", + tstamp->byte_offset, tstamp->copied_total); + if (stream->direction == SND_COMPRESS_PLAYBACK) + stream->runtime->total_bytes_transferred = tstamp->copied_total; + else + stream->runtime->total_bytes_available = tstamp->copied_total; + return 0; +} + +static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, + struct snd_compr_avail *avail) +{ + memset(avail, 0, sizeof(*avail)); + snd_compr_update_tstamp(stream, &avail->tstamp); + /* Still need to return avail even if tstamp can't be filled in */ + + if (stream->runtime->total_bytes_available == 0 && + stream->runtime->state == SNDRV_PCM_STATE_SETUP && + stream->direction == SND_COMPRESS_PLAYBACK) { + pr_debug("detected init and someone forgot to do a write\n"); + return stream->runtime->buffer_size; + } + pr_debug("app wrote %lld, DSP consumed %lld\n", + stream->runtime->total_bytes_available, + stream->runtime->total_bytes_transferred); + if (stream->runtime->total_bytes_available == + stream->runtime->total_bytes_transferred) { + if (stream->direction == SND_COMPRESS_PLAYBACK) { + pr_debug("both pointers are same, returning full avail\n"); + return stream->runtime->buffer_size; + } else { + pr_debug("both pointers are same, returning no avail\n"); + return 0; + } + } + + avail->avail = stream->runtime->total_bytes_available - + stream->runtime->total_bytes_transferred; + if (stream->direction == SND_COMPRESS_PLAYBACK) + avail->avail = stream->runtime->buffer_size - avail->avail; + + pr_debug("ret avail as %lld\n", avail->avail); + return avail->avail; +} + +static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) +{ + struct snd_compr_avail avail; + + return snd_compr_calc_avail(stream, &avail); +} + +static int +snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_avail ioctl_avail; + size_t avail; + + avail = snd_compr_calc_avail(stream, &ioctl_avail); + ioctl_avail.avail = avail; + + if (copy_to_user((__u64 __user *)arg, + &ioctl_avail, sizeof(ioctl_avail))) + return -EFAULT; + return 0; +} + +static int snd_compr_write_data(struct snd_compr_stream *stream, + const char __user *buf, size_t count) +{ + void *dstn; + size_t copy; + struct snd_compr_runtime *runtime = stream->runtime; + /* 64-bit Modulus */ + u64 app_pointer = div64_u64(runtime->total_bytes_available, + runtime->buffer_size); + app_pointer = runtime->total_bytes_available - + (app_pointer * runtime->buffer_size); + + dstn = runtime->buffer + app_pointer; + pr_debug("copying %ld at %lld\n", + (unsigned long)count, app_pointer); + if (count < runtime->buffer_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = runtime->buffer_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(runtime->buffer, buf + copy, count - copy)) + return -EFAULT; + } + /* if DSP cares, let it know data has been written */ + if (stream->ops->ack) + stream->ops->ack(stream, count); + return count; +} + +static ssize_t snd_compr_write(struct file *f, const char __user *buf, + size_t count, loff_t *offset) +{ + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + size_t avail; + int retval; + + if (snd_BUG_ON(!data)) + return -EFAULT; + + stream = &data->stream; + mutex_lock(&stream->device->lock); + /* write is allowed when stream is running or has been steup */ + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && + stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { + mutex_unlock(&stream->device->lock); + return -EBADFD; + } + + avail = snd_compr_get_avail(stream); + pr_debug("avail returned %ld\n", (unsigned long)avail); + /* calculate how much we can write to buffer */ + if (avail > count) + avail = count; + + if (stream->ops->copy) { + char __user* cbuf = (char __user*)buf; + retval = stream->ops->copy(stream, cbuf, avail); + } else { + retval = snd_compr_write_data(stream, buf, avail); + } + if (retval > 0) + stream->runtime->total_bytes_available += retval; + + /* while initiating the stream, write should be called before START + * call, so in setup move state */ + if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { + stream->runtime->state = SNDRV_PCM_STATE_PREPARED; + pr_debug("stream prepared, Houston we are good to go\n"); + } + + mutex_unlock(&stream->device->lock); + return retval; +} + + +static ssize_t snd_compr_read(struct file *f, char __user *buf, + size_t count, loff_t *offset) +{ + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + size_t avail; + int retval; + + if (snd_BUG_ON(!data)) + return -EFAULT; + + stream = &data->stream; + mutex_lock(&stream->device->lock); + + /* read is allowed when stream is running, paused, draining and setup + * (yes setup is state which we transition to after stop, so if user + * wants to read data after stop we allow that) + */ + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_XRUN: + case SNDRV_PCM_STATE_SUSPENDED: + case SNDRV_PCM_STATE_DISCONNECTED: + retval = -EBADFD; + goto out; + } + + avail = snd_compr_get_avail(stream); + pr_debug("avail returned %ld\n", (unsigned long)avail); + /* calculate how much we can read from buffer */ + if (avail > count) + avail = count; + + if (stream->ops->copy) { + retval = stream->ops->copy(stream, buf, avail); + } else { + retval = -ENXIO; + goto out; + } + if (retval > 0) + stream->runtime->total_bytes_transferred += retval; + +out: + mutex_unlock(&stream->device->lock); + return retval; +} + +static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) +{ + return -ENXIO; +} + +static inline int snd_compr_get_poll(struct snd_compr_stream *stream) +{ + if (stream->direction == SND_COMPRESS_PLAYBACK) + return POLLOUT | POLLWRNORM; + else + return POLLIN | POLLRDNORM; +} + +static unsigned int snd_compr_poll(struct file *f, poll_table *wait) +{ + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + size_t avail; + int retval = 0; + + if (snd_BUG_ON(!data)) + return -EFAULT; + stream = &data->stream; + if (snd_BUG_ON(!stream)) + return -EFAULT; + + mutex_lock(&stream->device->lock); + if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + retval = -EBADFD; + goto out; + } + poll_wait(f, &stream->runtime->sleep, wait); + + avail = snd_compr_get_avail(stream); + pr_debug("avail is %ld\n", (unsigned long)avail); + /* check if we have at least one fragment to fill */ + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_DRAINING: + /* stream has been woken up after drain is complete + * draining done so set stream state to stopped + */ + retval = snd_compr_get_poll(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + break; + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + if (avail >= stream->runtime->fragment_size) + retval = snd_compr_get_poll(stream); + break; + default: + if (stream->direction == SND_COMPRESS_PLAYBACK) + retval = POLLOUT | POLLWRNORM | POLLERR; + else + retval = POLLIN | POLLRDNORM | POLLERR; + break; + } +out: + mutex_unlock(&stream->device->lock); + return retval; +} + +static int +snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) +{ + int retval; + struct snd_compr_caps caps; + + if (!stream->ops->get_caps) + return -ENXIO; + + memset(&caps, 0, sizeof(caps)); + retval = stream->ops->get_caps(stream, &caps); + if (retval) + goto out; + if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) + retval = -EFAULT; +out: + return retval; +} + +static int +snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) +{ + int retval; + struct snd_compr_codec_caps *caps; + + if (!stream->ops->get_codec_caps) + return -ENXIO; + + caps = kzalloc(sizeof(*caps), GFP_KERNEL); + if (!caps) + return -ENOMEM; + + retval = stream->ops->get_codec_caps(stream, caps); + if (retval) + goto out; + if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) + retval = -EFAULT; + +out: + kfree(caps); + return retval; +} + +/* revisit this with snd_pcm_preallocate_xxx */ +static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + unsigned int buffer_size; + void *buffer; + + buffer_size = params->buffer.fragment_size * params->buffer.fragments; + if (stream->ops->copy) { + buffer = NULL; + /* if copy is defined the driver will be required to copy + * the data from core + */ + } else { + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + } + stream->runtime->fragment_size = params->buffer.fragment_size; + stream->runtime->fragments = params->buffer.fragments; + stream->runtime->buffer = buffer; + stream->runtime->buffer_size = buffer_size; + 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; + + return 0; +} + +static int +snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_params *params; + int retval; + + if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + /* + * we should allow parameter change only when stream has been + * opened not in other cases + */ + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { + 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; + + stream->metadata_set = false; + stream->next_track = false; + + if (stream->direction == SND_COMPRESS_PLAYBACK) + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + else + stream->runtime->state = SNDRV_PCM_STATE_PREPARED; + } else { + return -EPERM; + } +out: + kfree(params); + return retval; +} + +static int +snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_codec *params; + int retval; + + if (!stream->ops->get_params) + return -EBADFD; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + retval = stream->ops->get_params(stream, params); + if (retval) + goto out; + if (copy_to_user((char __user *)arg, params, sizeof(*params))) + retval = -EFAULT; + +out: + kfree(params); + return retval; +} + +static int +snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_metadata metadata; + int retval; + + if (!stream->ops->get_metadata) + return -ENXIO; + + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) + return -EFAULT; + + retval = stream->ops->get_metadata(stream, &metadata); + if (retval != 0) + return retval; + + if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) + return -EFAULT; + + return 0; +} + +static int +snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_metadata metadata; + int retval; + + if (!stream->ops->set_metadata) + return -ENXIO; + /* + * we should allow parameter change only when stream has been + * opened not in other cases + */ + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) + return -EFAULT; + + retval = stream->ops->set_metadata(stream, &metadata); + stream->metadata_set = true; + + return retval; +} + +static inline int +snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_tstamp tstamp = {0}; + int ret; + + ret = snd_compr_update_tstamp(stream, &tstamp); + if (ret == 0) + ret = copy_to_user((struct snd_compr_tstamp __user *)arg, + &tstamp, sizeof(tstamp)) ? -EFAULT : 0; + return ret; +} + +static int snd_compr_pause(struct snd_compr_stream *stream) +{ + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + return retval; +} + +static int snd_compr_resume(struct snd_compr_stream *stream) +{ + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + return retval; +} + +static int snd_compr_start(struct snd_compr_stream *stream) +{ + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + return retval; +} + +static int snd_compr_stop(struct snd_compr_stream *stream) +{ + int retval; + + if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || + stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); + if (!retval) { + snd_compr_drain_notify(stream); + stream->runtime->total_bytes_available = 0; + stream->runtime->total_bytes_transferred = 0; + } + return retval; +} + +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + int ret; + + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + /* we wait for drain to complete here, drain can return when + * interruption occurred, wait returned error or success. + * For the first two cases we don't do anything different here and + * return after waking up + */ + + ret = wait_event_interruptible(stream->runtime->sleep, + (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); + if (ret == -ERESTARTSYS) + pr_debug("wait aborted by a signal"); + else if (ret) + pr_debug("wait for drain failed with %d\n", ret); + + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return ret; +} + +static int snd_compr_drain(struct snd_compr_stream *stream) +{ + int retval; + + if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || + stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; + + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); + if (retval) { + pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); + wake_up(&stream->runtime->sleep); + return retval; + } + + return snd_compress_wait_for_drain(stream); +} + +static int snd_compr_next_track(struct snd_compr_stream *stream) +{ + int retval; + + /* only a running stream can transition to next track */ + if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + return -EPERM; + + /* you can signal next track isf this is intended to be a gapless stream + * and current track metadata is set + */ + if (stream->metadata_set == false) + return -EPERM; + + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); + if (retval != 0) + return retval; + stream->metadata_set = false; + stream->next_track = true; + return 0; +} + +static int snd_compr_partial_drain(struct snd_compr_stream *stream) +{ + int retval; + if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || + stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; + /* stream can be drained only when next track has been signalled */ + if (stream->next_track == false) + return -EPERM; + + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_debug("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } + + stream->next_track = false; + return snd_compress_wait_for_drain(stream); +} + +static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + int retval = -ENOTTY; + + if (snd_BUG_ON(!data)) + return -EFAULT; + stream = &data->stream; + if (snd_BUG_ON(!stream)) + return -EFAULT; + mutex_lock(&stream->device->lock); + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): + retval = put_user(SNDRV_COMPRESS_VERSION, + (int __user *)arg) ? -EFAULT : 0; + break; + case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): + retval = snd_compr_get_caps(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): + retval = snd_compr_get_codec_caps(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): + retval = snd_compr_set_params(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): + retval = snd_compr_get_params(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): + retval = snd_compr_set_metadata(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): + retval = snd_compr_get_metadata(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_TSTAMP): + retval = snd_compr_tstamp(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_AVAIL): + retval = snd_compr_ioctl_avail(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_PAUSE): + retval = snd_compr_pause(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_RESUME): + retval = snd_compr_resume(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_START): + retval = snd_compr_start(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_STOP): + retval = snd_compr_stop(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_DRAIN): + retval = snd_compr_drain(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): + retval = snd_compr_partial_drain(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): + retval = snd_compr_next_track(stream); + break; + + } + mutex_unlock(&stream->device->lock); + return retval; +} + +static const struct file_operations snd_compr_file_ops = { + .owner = THIS_MODULE, + .open = snd_compr_open, + .release = snd_compr_free, + .write = snd_compr_write, + .read = snd_compr_read, + .unlocked_ioctl = snd_compr_ioctl, + .mmap = snd_compr_mmap, + .poll = snd_compr_poll, +}; + +static int snd_compress_dev_register(struct snd_device *device) +{ + int ret = -EINVAL; + char str[16]; + struct snd_compr *compr; + + if (snd_BUG_ON(!device || !device->device_data)) + return -EBADFD; + compr = device->device_data; + + sprintf(str, "comprC%iD%i", compr->card->number, compr->device); + pr_debug("reg %s for device %s, direction %d\n", str, compr->name, + compr->direction); + /* register compressed device */ + ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, + compr->device, &snd_compr_file_ops, compr, str); + if (ret < 0) { + pr_err("snd_register_device failed\n %d", ret); + return ret; + } + return ret; + +} + +static int snd_compress_dev_disconnect(struct snd_device *device) +{ + struct snd_compr *compr; + + compr = device->device_data; + snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, + compr->device); + return 0; +} + +/* + * snd_compress_new: create new compress device + * @card: sound card pointer + * @device: device number + * @dirn: device direction, should be of type enum snd_compr_direction + * @compr: compress device pointer + */ +int snd_compress_new(struct snd_card *card, int device, + int dirn, struct snd_compr *compr) +{ + static struct snd_device_ops ops = { + .dev_free = NULL, + .dev_register = snd_compress_dev_register, + .dev_disconnect = snd_compress_dev_disconnect, + }; + + compr->card = card; + compr->device = device; + compr->direction = dirn; + return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); +} +EXPORT_SYMBOL_GPL(snd_compress_new); + +static int snd_compress_add_device(struct snd_compr *device) +{ + int ret; + + if (!device->card) + return -EINVAL; + + /* register the card */ + ret = snd_card_register(device->card); + if (ret) + goto out; + return 0; + +out: + pr_err("failed with %d\n", ret); + return ret; + +} + +static int snd_compress_remove_device(struct snd_compr *device) +{ + return snd_card_free(device->card); +} + +/** + * snd_compress_register - register compressed device + * + * @device: compressed device to register + */ +int snd_compress_register(struct snd_compr *device) +{ + int retval; + + if (device->name == NULL || device->dev == NULL || device->ops == NULL) + return -EINVAL; + + pr_debug("Registering compressed device %s\n", device->name); + if (snd_BUG_ON(!device->ops->open)) + return -EINVAL; + if (snd_BUG_ON(!device->ops->free)) + return -EINVAL; + if (snd_BUG_ON(!device->ops->set_params)) + return -EINVAL; + if (snd_BUG_ON(!device->ops->trigger)) + return -EINVAL; + + mutex_init(&device->lock); + + /* register a compressed card */ + mutex_lock(&device_mutex); + retval = snd_compress_add_device(device); + mutex_unlock(&device_mutex); + return retval; +} +EXPORT_SYMBOL_GPL(snd_compress_register); + +int snd_compress_deregister(struct snd_compr *device) +{ + pr_debug("Removing compressed device %s\n", device->name); + mutex_lock(&device_mutex); + snd_compress_remove_device(device); + mutex_unlock(&device_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_compress_deregister); + +static int __init snd_compress_init(void) +{ + return 0; +} + +static void __exit snd_compress_exit(void) +{ +} + +module_init(snd_compress_init); +module_exit(snd_compress_exit); + +MODULE_DESCRIPTION("ALSA Compressed offload framework"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/core/control.c b/sound/core/control.c index 01a1a5af47b..f0b0e14497a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -21,6 +21,7 @@ #include <linux/threads.h> #include <linux/interrupt.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/time.h> @@ -31,6 +32,7 @@ /* max number of user-defined controls */ #define MAX_USER_CONTROLS 32 +#define MAX_CONTROL_COUNT 1028 struct snd_kctl_ioctl { struct list_head list; /* list of all ioctls */ @@ -50,6 +52,10 @@ static int snd_ctl_open(struct inode *inode, struct file *file) struct snd_ctl_file *ctl; int err; + err = nonseekable_open(inode, file); + if (err < 0) + return err; + card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); if (!card) { err = -ENODEV; @@ -75,11 +81,12 @@ static int snd_ctl_open(struct inode *inode, struct file *file) ctl->card = card; ctl->prefer_pcm_subdevice = -1; ctl->prefer_rawmidi_subdevice = -1; - ctl->pid = current->pid; + ctl->pid = get_pid(task_pid(current)); file->private_data = ctl; write_lock_irqsave(&card->ctl_files_rwlock, flags); list_add_tail(&ctl->list, &card->ctl_files); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); + snd_card_unref(card); return 0; __error: @@ -87,6 +94,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file) __error2: snd_card_file_remove(card, file); __error1: + if (card) + snd_card_unref(card); return err; } @@ -113,7 +122,6 @@ static int snd_ctl_release(struct inode *inode, struct file *file) unsigned int idx; ctl = file->private_data; - fasync_helper(-1, file, 0, &ctl->fasync); file->private_data = NULL; card = ctl->card; write_lock_irqsave(&card->ctl_files_rwlock, flags); @@ -126,6 +134,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file) control->vd[idx].owner = NULL; up_write(&card->controls_rwsem); snd_ctl_empty_read_queue(ctl); + put_pid(ctl->pid); kfree(ctl); module_put(card->module); snd_card_file_remove(card, file); @@ -139,9 +148,10 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_file *ctl; struct snd_kctl_event *ev; - snd_assert(card != NULL && id != NULL, return); + if (snd_BUG_ON(!card || !id)) + return; read_lock(&card->ctl_files_rwlock); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; #endif list_for_each_entry(ctl, &card->ctl_files, list) { @@ -160,7 +170,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, ev->mask = mask; list_add_tail(&ev->list, &ctl->events); } else { - snd_printk(KERN_ERR "No memory available to allocate event\n"); + dev_err(card->dev, "No memory available to allocate event\n"); } _found: wake_up(&ctl->change_sleep); @@ -180,7 +190,7 @@ EXPORT_SYMBOL(snd_ctl_notify); * Allocates a new struct snd_kcontrol instance and copies the given template * to the new instance. It does not copy volatile data (access). * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access) @@ -188,11 +198,15 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, struct snd_kcontrol *kctl; unsigned int idx; - snd_assert(control != NULL, return NULL); - snd_assert(control->count > 0, return NULL); + if (snd_BUG_ON(!control || !control->count)) + return NULL; + + if (control->count > MAX_CONTROL_COUNT) + return NULL; + kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); if (kctl == NULL) { - snd_printk(KERN_ERR "Cannot allocate control instance\n"); + pr_err("ALSA: Cannot allocate control instance\n"); return NULL; } *kctl = *control; @@ -210,7 +224,7 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, * template. When the access field of ncontrol is 0, it's assumed as * READWRITE access. When the count field is 0, it's assumes as one. * - * Returns the pointer of the newly generated instance, or NULL on failure. + * Return: The pointer of the newly generated instance, or %NULL on failure. */ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) @@ -218,21 +232,27 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, struct snd_kcontrol kctl; unsigned int access; - snd_assert(ncontrol != NULL, return NULL); - snd_assert(ncontrol->info != NULL, return NULL); + if (snd_BUG_ON(!ncontrol || !ncontrol->info)) + return NULL; memset(&kctl, 0, sizeof(kctl)); kctl.id.iface = ncontrol->iface; kctl.id.device = ncontrol->device; kctl.id.subdevice = ncontrol->subdevice; - if (ncontrol->name) + if (ncontrol->name) { strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); + if (strcmp(ncontrol->name, kctl.id.name) != 0) + pr_warn("ALSA: Control name '%s' truncated to '%s'\n", + ncontrol->name, kctl.id.name); + } kctl.id.index = ncontrol->index; 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_CALLBACK)); + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; @@ -263,33 +283,35 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol) EXPORT_SYMBOL(snd_ctl_free_one); -static unsigned int snd_ctl_hole_check(struct snd_card *card, - unsigned int count) +static bool snd_ctl_remove_numid_conflict(struct snd_card *card, + unsigned int count) { struct snd_kcontrol *kctl; + /* Make sure that the ids assigned to the control do not wrap around */ + if (card->last_numid >= UINT_MAX - count) + card->last_numid = 0; + list_for_each_entry(kctl, &card->controls, list) { - if ((kctl->id.numid <= card->last_numid && - kctl->id.numid + kctl->count > card->last_numid) || - (kctl->id.numid <= card->last_numid + count - 1 && - kctl->id.numid + kctl->count > card->last_numid + count - 1)) - return card->last_numid = kctl->id.numid + kctl->count - 1; + if (kctl->id.numid < card->last_numid + 1 + count && + kctl->id.numid + kctl->count > card->last_numid + 1) { + card->last_numid = kctl->id.numid + kctl->count - 1; + return true; + } } - return card->last_numid; + return false; } static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) { - unsigned int last_numid, iter = 100000; + unsigned int iter = 100000; - last_numid = card->last_numid; - while (last_numid != snd_ctl_hole_check(card, count)) { + while (snd_ctl_remove_numid_conflict(card, count)) { if (--iter == 0) { /* this situation is very unlikely */ - snd_printk(KERN_ERR "unable to allocate new control numid\n"); + dev_err(card->dev, "unable to allocate new control numid\n"); return -ENOMEM; } - last_numid = card->last_numid; } return 0; } @@ -303,25 +325,30 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) * snd_ctl_new1() to the given card. Assigns also an unique * numid used for fast search. * - * Returns zero if successful, or a negative error code on failure. - * * It frees automatically the control which cannot be added. + * + * Return: Zero if successful, or a negative error code on failure. + * */ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) { struct snd_ctl_elem_id id; unsigned int idx; + unsigned int count; int err = -EINVAL; if (! kcontrol) return err; - snd_assert(card != NULL, goto error); - snd_assert(kcontrol->info != NULL, goto error); + if (snd_BUG_ON(!card || !kcontrol->info)) + goto error; id = kcontrol->id; + if (id.index > UINT_MAX - kcontrol->count) + goto error; + down_write(&card->controls_rwsem); if (snd_ctl_find_id(card, &id)) { up_write(&card->controls_rwsem); - snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", + dev_err(card->dev, "control %i:%i:%i:%s:%i is already present\n", id.iface, id.device, id.subdevice, @@ -339,8 +366,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; + count = kcontrol->count; up_write(&card->controls_rwsem); - for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) + for (idx = 0; idx < count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); return 0; @@ -352,6 +380,72 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) EXPORT_SYMBOL(snd_ctl_add); /** + * snd_ctl_replace - replace the control instance of the card + * @card: the card instance + * @kcontrol: the control instance to replace + * @add_on_replace: add the control if not already added + * + * Replaces the given control. If the given control does not exist + * and the add_on_replace flag is set, the control is added. If the + * control exists, it is destroyed first. + * + * It frees automatically the control which cannot be added or replaced. + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, + bool add_on_replace) +{ + struct snd_ctl_elem_id id; + unsigned int count; + unsigned int idx; + struct snd_kcontrol *old; + int ret; + + if (!kcontrol) + return -EINVAL; + if (snd_BUG_ON(!card || !kcontrol->info)) { + ret = -EINVAL; + goto error; + } + id = kcontrol->id; + down_write(&card->controls_rwsem); + old = snd_ctl_find_id(card, &id); + if (!old) { + if (add_on_replace) + goto add; + up_write(&card->controls_rwsem); + ret = -EINVAL; + goto error; + } + ret = snd_ctl_remove(card, old); + if (ret < 0) { + up_write(&card->controls_rwsem); + goto error; + } +add: + if (snd_ctl_find_hole(card, kcontrol->count) < 0) { + up_write(&card->controls_rwsem); + ret = -ENOMEM; + goto error; + } + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count += kcontrol->count; + kcontrol->id.numid = card->last_numid + 1; + card->last_numid += kcontrol->count; + count = kcontrol->count; + up_write(&card->controls_rwsem); + for (idx = 0; idx < count; idx++, id.index++, id.numid++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); + return 0; + +error: + snd_ctl_free_one(kcontrol); + return ret; +} +EXPORT_SYMBOL(snd_ctl_replace); + +/** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance * @kcontrol: the control instance to remove @@ -359,15 +453,16 @@ EXPORT_SYMBOL(snd_ctl_add); * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). You must be in * the write lock - down_write(&card->controls_rwsem). - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { struct snd_ctl_elem_id id; unsigned int idx; - snd_assert(card != NULL && kcontrol != NULL, return -EINVAL); + if (snd_BUG_ON(!card || !kcontrol)) + return -EINVAL; list_del(&kcontrol->list); card->controls_count -= kcontrol->count; id = kcontrol->id; @@ -386,8 +481,8 @@ EXPORT_SYMBOL(snd_ctl_remove); * * Finds the control instance with the given id, removes it from the * card list and releases it. - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) { @@ -408,17 +503,17 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) EXPORT_SYMBOL(snd_ctl_remove_id); /** - * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it + * snd_ctl_remove_user_ctl - remove and release the unlocked user control * @file: active control handle * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ -static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, - struct snd_ctl_elem_id *id) +static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, + struct snd_ctl_elem_id *id) { struct snd_card *card = file->card; struct snd_kcontrol *kctl; @@ -427,20 +522,74 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { - up_write(&card->controls_rwsem); - return -ENOENT; + ret = -ENOENT; + goto error; + } + if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) { + ret = -EINVAL; + goto error; } for (idx = 0; idx < kctl->count; idx++) if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { - up_write(&card->controls_rwsem); - return -EBUSY; + ret = -EBUSY; + goto error; } ret = snd_ctl_remove(card, kctl); + if (ret < 0) + goto error; + card->user_ctl_count--; +error: up_write(&card->controls_rwsem); return ret; } /** + * snd_ctl_activate_id - activate/inactivate the control of the given id + * @card: the card instance + * @id: the control id to activate/inactivate + * @active: non-zero to activate + * + * Finds the control instance with the given id, and activate or + * inactivate the control together with notification, if changed. + * + * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. + */ +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, + int active) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_volatile *vd; + unsigned int index_offset; + int ret; + + down_write(&card->controls_rwsem); + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) { + ret = -ENOENT; + goto unlock; + } + index_offset = snd_ctl_get_ioff(kctl, &kctl->id); + vd = &kctl->vd[index_offset]; + ret = 0; + if (active) { + if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + goto unlock; + vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } else { + if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) + goto unlock; + vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } + ret = 1; + unlock: + up_write(&card->controls_rwsem); + if (ret > 0) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id); + return ret; +} +EXPORT_SYMBOL_GPL(snd_ctl_activate_id); + +/** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance * @src_id: the old id @@ -449,7 +598,7 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, * Finds the control with the old id from the card, and replaces the * id with the new one. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id) @@ -478,16 +627,18 @@ EXPORT_SYMBOL(snd_ctl_rename_id); * * Finds the control instance with the given number-id from the card. * - * Returns the pointer of the instance if found, or NULL if not. - * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). + * + * Return: The pointer of the instance if found, or %NULL if not. + * */ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) { struct snd_kcontrol *kctl; - snd_assert(card != NULL && numid != 0, return NULL); + if (snd_BUG_ON(!card || !numid)) + return NULL; list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) return kctl; @@ -504,17 +655,19 @@ EXPORT_SYMBOL(snd_ctl_find_numid); * * Finds the control instance with the given id from the card. * - * Returns the pointer of the instance if found, or NULL if not. - * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). + * + * Return: The pointer of the instance if found, or %NULL if not. + * */ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, struct snd_ctl_elem_id *id) { struct snd_kcontrol *kctl; - snd_assert(card != NULL && id != NULL, return NULL); + if (snd_BUG_ON(!card || !id)) + return NULL; if (id->numid != 0) return snd_ctl_find_numid(card, id->numid); list_for_each_entry(kctl, &card->controls, list) { @@ -569,13 +722,12 @@ static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list list; struct snd_kcontrol *kctl; struct snd_ctl_elem_id *dst, *id; - unsigned int offset, space, first, jidx; + unsigned int offset, space, jidx; if (copy_from_user(&list, _list, sizeof(list))) return -EFAULT; offset = list.offset; space = list.space; - first = 0; /* try limit maximum space */ if (space > 16384) return -ENOMEM; @@ -647,7 +799,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, #endif result = kctl->info(kctl, info); if (result >= 0) { - snd_assert(info->access == 0, ); + snd_BUG_ON(info->access); index_offset = snd_ctl_get_ioff(kctl, &info->id); vd = &kctl->vd[index_offset]; snd_ctl_build_ioff(&info->id, kctl, index_offset); @@ -656,7 +808,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK; if (vd->owner == ctl) info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER; - info->owner = vd->owner_pid; + info->owner = pid_vnr(vd->owner->pid); } else { info->owner = -1; } @@ -684,7 +836,8 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, return result; } -int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control) +static int snd_ctl_elem_read(struct snd_card *card, + struct snd_ctl_elem_value *control) { struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; @@ -714,14 +867,11 @@ static int snd_ctl_elem_read_user(struct snd_card *card, { struct snd_ctl_elem_value *control; int result; - - control = kmalloc(sizeof(*control), GFP_KERNEL); - if (control == NULL) - return -ENOMEM; - if (copy_from_user(control, _control, sizeof(*control))) { - kfree(control); - return -EFAULT; - } + + control = memdup_user(_control, sizeof(*control)); + if (IS_ERR(control)) + return PTR_ERR(control); + snd_power_lock(card); result = snd_power_wait(card, SNDRV_CTL_POWER_D0); if (result >= 0) @@ -734,8 +884,8 @@ static int snd_ctl_elem_read_user(struct snd_card *card, return result; } -int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, - struct snd_ctl_elem_value *control) +static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, + struct snd_ctl_elem_value *control) { struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; @@ -758,9 +908,9 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, result = kctl->put(kctl, control); } if (result > 0) { + struct snd_ctl_elem_id id = control->id; up_read(&card->controls_rwsem); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &control->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); return 0; } } @@ -775,13 +925,10 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_card *card; int result; - control = kmalloc(sizeof(*control), GFP_KERNEL); - if (control == NULL) - return -ENOMEM; - if (copy_from_user(control, _control, sizeof(*control))) { - kfree(control); - return -EFAULT; - } + control = memdup_user(_control, sizeof(*control)); + if (IS_ERR(control)) + return PTR_ERR(control); + card = file->card; snd_power_lock(card); result = snd_power_wait(card, SNDRV_CTL_POWER_D0); @@ -816,7 +963,6 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file, result = -EBUSY; else { vd->owner = file; - vd->owner_pid = current->pid; result = 0; } } @@ -847,7 +993,6 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, result = -EPERM; else { vd->owner = NULL; - vd->owner_pid = 0; result = 0; } } @@ -857,12 +1002,12 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct user_element { struct snd_ctl_elem_info info; + struct snd_card *card; void *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ unsigned long tlv_data_size; /* TLV data size */ void *priv_data; /* private data (like strings for enumerated type) */ - unsigned long priv_data_size; /* size of private data in bytes */ }; static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, @@ -874,12 +1019,36 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, return 0; } +static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct user_element *ue = kcontrol->private_data; + const char *names; + unsigned int item; + + item = uinfo->value.enumerated.item; + + *uinfo = ue->info; + + item = min(item, uinfo->value.enumerated.items - 1); + uinfo->value.enumerated.item = item; + + names = ue->priv_data; + for (; item > 0; --item) + names += strlen(names) + 1; + strcpy(uinfo->value.enumerated.name, names); + + return 0; +} + static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct user_element *ue = kcontrol->private_data; + mutex_lock(&ue->card->user_ctl_lock); memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); + mutex_unlock(&ue->card->user_ctl_lock); return 0; } @@ -888,10 +1057,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, { int change; struct user_element *ue = kcontrol->private_data; - + + mutex_lock(&ue->card->user_ctl_lock); change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; if (change) memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); + mutex_unlock(&ue->card->user_ctl_lock); return change; } @@ -907,35 +1078,80 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, if (op_flag > 0) { if (size > 1024 * 128) /* sane value */ return -EINVAL; - new_data = kmalloc(size, GFP_KERNEL); - if (new_data == NULL) - return -ENOMEM; - if (copy_from_user(new_data, tlv, size)) { - kfree(new_data); - return -EFAULT; - } + + new_data = memdup_user(tlv, size); + if (IS_ERR(new_data)) + return PTR_ERR(new_data); + mutex_lock(&ue->card->user_ctl_lock); change = ue->tlv_data_size != size; if (!change) change = memcmp(ue->tlv_data, new_data, size); kfree(ue->tlv_data); ue->tlv_data = new_data; ue->tlv_data_size = size; + mutex_unlock(&ue->card->user_ctl_lock); } else { - if (! ue->tlv_data_size || ! ue->tlv_data) - return -ENXIO; - if (size < ue->tlv_data_size) - return -ENOSPC; + int ret = 0; + + mutex_lock(&ue->card->user_ctl_lock); + if (!ue->tlv_data_size || !ue->tlv_data) { + ret = -ENXIO; + goto err_unlock; + } + if (size < ue->tlv_data_size) { + ret = -ENOSPC; + goto err_unlock; + } if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) - return -EFAULT; + ret = -EFAULT; +err_unlock: + mutex_unlock(&ue->card->user_ctl_lock); + if (ret) + return ret; } return change; } +static int snd_ctl_elem_init_enum_names(struct user_element *ue) +{ + char *names, *p; + size_t buf_len, name_len; + unsigned int i; + const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr; + + if (ue->info.value.enumerated.names_length > 64 * 1024) + return -EINVAL; + + names = memdup_user((const void __user *)user_ptrval, + ue->info.value.enumerated.names_length); + if (IS_ERR(names)) + return PTR_ERR(names); + + /* check that there are enough valid names */ + buf_len = ue->info.value.enumerated.names_length; + p = names; + for (i = 0; i < ue->info.value.enumerated.items; ++i) { + name_len = strnlen(p, buf_len); + if (name_len == 0 || name_len >= 64 || name_len == buf_len) { + kfree(names); + return -EINVAL; + } + p += name_len + 1; + buf_len -= name_len + 1; + } + + ue->priv_data = names; + ue->info.value.enumerated.names_ptr = 0; + + return 0; +} + static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) { struct user_element *ue = kcontrol->private_data; - if (ue->tlv_data) - kfree(ue->tlv_data); + + kfree(ue->tlv_data); + kfree(ue->priv_data); kfree(ue); } @@ -948,10 +1164,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, long private_size; struct user_element *ue; int idx, err; - - if (card->user_ctl_count >= MAX_USER_CONTROLS) - return -ENOMEM; - if (info->count > 1024) + + if (info->count < 1) return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| @@ -959,25 +1173,23 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); info->id.numid = 0; memset(&kctl, 0, sizeof(kctl)); - down_write(&card->controls_rwsem); - _kctl = snd_ctl_find_id(card, &info->id); - err = 0; - if (_kctl) { - if (replace) - err = snd_ctl_remove(card, _kctl); - else - err = -EBUSY; - } else { - if (replace) - err = -ENOENT; + + if (replace) { + err = snd_ctl_remove_user_ctl(file, &info->id); + if (err) + return err; } - up_write(&card->controls_rwsem); - if (err < 0) - return err; + + if (card->user_ctl_count >= MAX_USER_CONTROLS) + return -ENOMEM; + memcpy(&kctl.id, &info->id, sizeof(info->id)); kctl.count = info->owner ? info->owner : 1; access |= SNDRV_CTL_ELEM_ACCESS_USER; - kctl.info = snd_ctl_elem_user_info; + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) + kctl.info = snd_ctl_elem_user_enum_info; + else + kctl.info = snd_ctl_elem_user_info; if (access & SNDRV_CTL_ELEM_ACCESS_READ) kctl.get = snd_ctl_elem_user_get; if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) @@ -998,6 +1210,11 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count > 64) return -EINVAL; break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + private_size = sizeof(unsigned int); + if (info->count > 128 || info->value.enumerated.items == 0) + return -EINVAL; + break; case SNDRV_CTL_ELEM_TYPE_BYTES: private_size = sizeof(unsigned char); if (info->count > 512) @@ -1015,13 +1232,22 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; + ue->card = card; ue->info = *info; ue->info.access = 0; ue->elem_data = (char *)ue + sizeof(*ue); ue->elem_data_size = private_size; + if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { + err = snd_ctl_elem_init_enum_names(ue); + if (err < 0) { + kfree(ue); + return err; + } + } kctl.private_free = snd_ctl_elem_user_free; _kctl = snd_ctl_new(&kctl, access); if (_kctl == NULL) { + kfree(ue->priv_data); kfree(ue); return -ENOMEM; } @@ -1052,18 +1278,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file, struct snd_ctl_elem_id __user *_id) { struct snd_ctl_elem_id id; - int err; if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; - err = snd_ctl_remove_unlocked_id(file, &id); - if (! err) { - struct snd_card *card = file->card; - down_write(&card->controls_rwsem); - card->user_ctl_count--; - up_write(&card->controls_rwsem); - } - return err; + return snd_ctl_remove_user_ctl(file, &id); } static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr) @@ -1100,7 +1318,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, if (copy_from_user(&tlv, _tlv, sizeof(tlv))) return -EFAULT; - if (tlv.length < sizeof(unsigned int) * 3) + if (tlv.length < sizeof(unsigned int) * 2) return -EINVAL; down_read(&card->controls_rwsem); kctl = snd_ctl_find_numid(card, tlv.numid); @@ -1120,14 +1338,15 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, goto __kctl_end; } if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - if (file && vd->owner != NULL && vd->owner != file) { + if (vd->owner != NULL && vd->owner != file) { err = -EPERM; goto __kctl_end; } - err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); + err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); if (err > 0) { + struct snd_ctl_elem_id id = kctl->id; up_read(&card->controls_rwsem); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id); return 0; } } else { @@ -1159,7 +1378,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg ctl = file->private_data; card = ctl->card; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; switch (cmd) { case SNDRV_CTL_IOCTL_PVERSION: return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; @@ -1209,7 +1429,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg } } up_read(&snd_ioctl_rwsem); - snd_printdd("unknown ioctl = 0x%x\n", cmd); + dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } @@ -1221,7 +1441,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer, ssize_t result = 0; ctl = file->private_data; - snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); + if (snd_BUG_ON(!ctl || !ctl->card)) + return -ENXIO; if (!ctl->subscribed) return -EBADFD; if (count < sizeof(struct snd_ctl_event)) @@ -1242,6 +1463,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer, spin_unlock_irq(&ctl->read_lock); schedule(); remove_wait_queue(&ctl->change_sleep, &wait); + if (ctl->card->shutdown) + return -ENODEV; if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irq(&ctl->read_lock); @@ -1327,7 +1550,8 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, { struct snd_kctl_ioctl *p; - snd_assert(fcn != NULL, return -EINVAL); + if (snd_BUG_ON(!fcn)) + return -EINVAL; down_write(&snd_ioctl_rwsem); list_for_each_entry(p, lists, list) { if (p->fioctl == fcn) { @@ -1361,12 +1585,9 @@ EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); static int snd_ctl_fasync(int fd, struct file * file, int on) { struct snd_ctl_file *ctl; - int err; + ctl = file->private_data; - err = fasync_helper(fd, file, on, &ctl->fasync); - if (err < 0) - return err; - return 0; + return fasync_helper(fd, file, on, &ctl->fasync); } /* @@ -1388,6 +1609,7 @@ static const struct file_operations snd_ctl_f_ops = .read = snd_ctl_read, .open = snd_ctl_open, .release = snd_ctl_release, + .llseek = no_llseek, .poll = snd_ctl_poll, .unlocked_ioctl = snd_ctl_ioctl, .compat_ioctl = snd_ctl_ioctl_compat, @@ -1403,9 +1625,11 @@ static int snd_ctl_dev_register(struct snd_device *device) int err, cardnum; char name[16]; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; cardnum = card->number; - snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) + return -ENXIO; sprintf(name, "controlC%i", cardnum); if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name)) < 0) @@ -1422,16 +1646,18 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) struct snd_ctl_file *ctl; int err, cardnum; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; cardnum = card->number; - snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) + return -ENXIO; - down_read(&card->controls_rwsem); + read_lock(&card->ctl_files_rwlock); list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } - up_read(&card->controls_rwsem); + read_unlock(&card->ctl_files_rwlock); if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1)) < 0) @@ -1468,12 +1694,13 @@ int snd_ctl_create(struct snd_card *card) .dev_disconnect = snd_ctl_dev_disconnect, }; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); } /* - * Frequently used control callbacks + * Frequently used control callbacks/helpers */ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1498,3 +1725,31 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); + +/** + * snd_ctl_enum_info - fills the info structure for an enumerated control + * @info: the structure to be filled + * @channels: the number of the control's channels; often one + * @items: the number of control values; also the size of @names + * @names: an array containing the names of all control values + * + * Sets all required fields in @info to their appropriate values. + * If the control's accessibility is not the default (readable and writable), + * the caller has to fill @info->access. + * + * Return: Zero. + */ +int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels, + unsigned int items, const char *const names[]) +{ + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = channels; + info->value.enumerated.items = items; + if (info->value.enumerated.item >= items) + info->value.enumerated.item = items - 1; + strlcpy(info->value.enumerated.name, + names[info->value.enumerated.item], + sizeof(info->value.enumerated.name)); + return 0; +} +EXPORT_SYMBOL(snd_ctl_enum_info); diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 6101259ad86..b9c0910fb8c 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -21,6 +21,7 @@ /* this file included from control.c */ #include <linux/compat.h> +#include <linux/slab.h> struct snd_ctl_elem_list32 { u32 offset; @@ -82,6 +83,8 @@ struct snd_ctl_elem_info32 { u32 items; u32 item; char name[64]; + u64 names_ptr; + u32 names_length; } enumerated; unsigned char reserved[128]; } value; @@ -244,7 +247,7 @@ static int copy_ctl_value_from_user(struct snd_card *card, } else { size = get_elem_size(type, count); if (size < 0) { - printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); + dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); return -EINVAL; } if (copy_from_user(data->value.bytes.data, @@ -371,6 +374,8 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, &data32->value.enumerated, sizeof(data->value.enumerated))) goto error; + data->value.enumerated.names_ptr = + (uintptr_t)compat_ptr(data->value.enumerated.names_ptr); break; default: break; @@ -398,7 +403,8 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns int err; ctl = file->private_data; - snd_assert(ctl && ctl->card, return -ENXIO); + if (snd_BUG_ON(!ctl || !ctl->card)) + return -ENXIO; switch (cmd) { case SNDRV_CTL_IOCTL_PVERSION: diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c new file mode 100644 index 00000000000..e4b38fbe51d --- /dev/null +++ b/sound/core/ctljack.c @@ -0,0 +1,56 @@ +/* + * Helper functions for jack-detection kcontrols + * + * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <sound/core.h> +#include <sound/control.h> + +#define jack_detect_kctl_info snd_ctl_boolean_mono_info + +static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = kcontrol->private_value; + return 0; +} + +static struct snd_kcontrol_new jack_detect_kctl = { + /* name is filled later */ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = jack_detect_kctl_info, + .get = jack_detect_kctl_get, +}; + +struct snd_kcontrol * +snd_kctl_jack_new(const char *name, int idx, void *private_data) +{ + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(&jack_detect_kctl, private_data); + if (!kctl) + return NULL; + snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name); + kctl->id.index = idx; + kctl->private_value = 0; + return kctl; +} +EXPORT_SYMBOL_GPL(snd_kctl_jack_new); + +void snd_kctl_jack_report(struct snd_card *card, + struct snd_kcontrol *kctl, bool status) +{ + if (kctl->private_value == status) + return; + kctl->private_value = status; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} +EXPORT_SYMBOL_GPL(snd_kctl_jack_report); diff --git a/sound/core/device.c b/sound/core/device.c index 202dac0e4d8..41bec3075ae 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -21,6 +21,7 @@ #include <linux/slab.h> #include <linux/time.h> +#include <linux/export.h> #include <linux/errno.h> #include <sound/core.h> @@ -38,32 +39,75 @@ * The data pointer plays a role as the identifier, too, so the * pointer address must be unique and unchanged. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ -int snd_device_new(struct snd_card *card, snd_device_type_t type, +int snd_device_new(struct snd_card *card, enum snd_device_type type, void *device_data, struct snd_device_ops *ops) { struct snd_device *dev; + struct list_head *p; - snd_assert(card != NULL, return -ENXIO); - snd_assert(device_data != NULL, return -ENXIO); - snd_assert(ops != NULL, return -ENXIO); + if (snd_BUG_ON(!card || !device_data || !ops)) + return -ENXIO; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { - snd_printk(KERN_ERR "Cannot allocate device\n"); + dev_err(card->dev, "Cannot allocate device, type=%d\n", type); return -ENOMEM; } + INIT_LIST_HEAD(&dev->list); dev->card = card; dev->type = type; dev->state = SNDRV_DEV_BUILD; dev->device_data = device_data; dev->ops = ops; - list_add(&dev->list, &card->devices); /* add to the head of list */ + + /* insert the entry in an incrementally sorted list */ + list_for_each_prev(p, &card->devices) { + struct snd_device *pdev = list_entry(p, struct snd_device, list); + if ((unsigned int)pdev->type <= (unsigned int)type) + break; + } + + list_add(&dev->list, p); return 0; } - EXPORT_SYMBOL(snd_device_new); +static int __snd_device_disconnect(struct snd_device *dev) +{ + if (dev->state == SNDRV_DEV_REGISTERED) { + if (dev->ops->dev_disconnect && + dev->ops->dev_disconnect(dev)) + dev_err(dev->card->dev, "device disconnect failure\n"); + dev->state = SNDRV_DEV_DISCONNECTED; + } + return 0; +} + +static void __snd_device_free(struct snd_device *dev) +{ + /* unlink */ + list_del(&dev->list); + + __snd_device_disconnect(dev); + if (dev->ops->dev_free) { + if (dev->ops->dev_free(dev)) + dev_err(dev->card->dev, "device free failure\n"); + } + kfree(dev); +} + +static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + list_for_each_entry(dev, &card->devices, list) + if (dev->device_data == device_data) + return dev; + + return NULL; +} + /** * snd_device_free - release the device from the card * @card: the card instance @@ -72,73 +116,33 @@ EXPORT_SYMBOL(snd_device_new); * Removes the device from the list on the card and invokes the * callbacks, dev_disconnect and dev_free, corresponding to the state. * Then release the device. - * - * Returns zero if successful, or a negative error code on failure or if the - * device not found. */ -int snd_device_free(struct snd_card *card, void *device_data) +void snd_device_free(struct snd_card *card, void *device_data) { struct snd_device *dev; - snd_assert(card != NULL, return -ENXIO); - snd_assert(device_data != NULL, return -ENXIO); - list_for_each_entry(dev, &card->devices, list) { - if (dev->device_data != device_data) - continue; - /* unlink */ - list_del(&dev->list); - if (dev->state == SNDRV_DEV_REGISTERED && - dev->ops->dev_disconnect) - if (dev->ops->dev_disconnect(dev)) - snd_printk(KERN_ERR - "device disconnect failure\n"); - if (dev->ops->dev_free) { - if (dev->ops->dev_free(dev)) - snd_printk(KERN_ERR "device free failure\n"); - } - kfree(dev); - return 0; - } - snd_printd("device free %p (from %p), not found\n", device_data, - __builtin_return_address(0)); - return -ENXIO; + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_free(dev); + else + dev_dbg(card->dev, "device free %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); } - EXPORT_SYMBOL(snd_device_free); -/** - * snd_device_disconnect - disconnect the device - * @card: the card instance - * @device_data: the data pointer to disconnect - * - * Turns the device into the disconnection state, invoking - * dev_disconnect callback, if the device was already registered. - * - * Usually called from snd_card_disconnect(). - * - * Returns zero if successful, or a negative error code on failure or if the - * device not found. - */ -int snd_device_disconnect(struct snd_card *card, void *device_data) +static int __snd_device_register(struct snd_device *dev) { - struct snd_device *dev; - - snd_assert(card != NULL, return -ENXIO); - snd_assert(device_data != NULL, return -ENXIO); - list_for_each_entry(dev, &card->devices, list) { - if (dev->device_data != device_data) - continue; - if (dev->state == SNDRV_DEV_REGISTERED && - dev->ops->dev_disconnect) { - if (dev->ops->dev_disconnect(dev)) - snd_printk(KERN_ERR "device disconnect failure\n"); - dev->state = SNDRV_DEV_DISCONNECTED; + if (dev->state == SNDRV_DEV_BUILD) { + if (dev->ops->dev_register) { + int err = dev->ops->dev_register(dev); + if (err < 0) + return err; } - return 0; + dev->state = SNDRV_DEV_REGISTERED; } - snd_printd("device disconnect %p (from %p), not found\n", device_data, - __builtin_return_address(0)); - return -ENXIO; + return 0; } /** @@ -151,32 +155,21 @@ int snd_device_disconnect(struct snd_card *card, void *device_data) * but it can be called later if any new devices are created after * invocation of snd_card_register(). * - * Returns zero if successful, or a negative error code on failure or if the + * Return: Zero if successful, or a negative error code on failure or if the * device not found. */ int snd_device_register(struct snd_card *card, void *device_data) { struct snd_device *dev; - int err; - snd_assert(card != NULL, return -ENXIO); - snd_assert(device_data != NULL, return -ENXIO); - list_for_each_entry(dev, &card->devices, list) { - if (dev->device_data != device_data) - continue; - if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { - if ((err = dev->ops->dev_register(dev)) < 0) - return err; - dev->state = SNDRV_DEV_REGISTERED; - return 0; - } - snd_printd("snd_device_register busy\n"); - return -EBUSY; - } + if (snd_BUG_ON(!card || !device_data)) + return -ENXIO; + dev = look_for_dev(card, device_data); + if (dev) + return __snd_device_register(dev); snd_BUG(); return -ENXIO; } - EXPORT_SYMBOL(snd_device_register); /* @@ -188,13 +181,12 @@ int snd_device_register_all(struct snd_card *card) struct snd_device *dev; int err; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; list_for_each_entry(dev, &card->devices, list) { - if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { - if ((err = dev->ops->dev_register(dev)) < 0) - return err; - dev->state = SNDRV_DEV_REGISTERED; - } + err = __snd_device_register(dev); + if (err < 0) + return err; } return 0; } @@ -208,9 +200,10 @@ int snd_device_disconnect_all(struct snd_card *card) struct snd_device *dev; int err = 0; - snd_assert(card != NULL, return -ENXIO); - list_for_each_entry(dev, &card->devices, list) { - if (snd_device_disconnect(card, dev->device_data) < 0) + if (snd_BUG_ON(!card)) + return -ENXIO; + list_for_each_entry_reverse(dev, &card->devices, list) { + if (__snd_device_disconnect(dev) < 0) err = -ENXIO; } return err; @@ -220,22 +213,12 @@ int snd_device_disconnect_all(struct snd_card *card) * release all the devices on the card. * called from init.c */ -int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) +void snd_device_free_all(struct snd_card *card) { - struct snd_device *dev; - int err; - unsigned int range_low, range_high; + struct snd_device *dev, *next; - snd_assert(card != NULL, return -ENXIO); - range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; - range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; - __again: - list_for_each_entry(dev, &card->devices, list) { - if (dev->type >= range_low && dev->type <= range_high) { - if ((err = snd_device_free(card, dev->device_data)) < 0) - return err; - goto __again; - } - } - return 0; + if (snd_BUG_ON(!card)) + return; + list_for_each_entry_safe_reverse(dev, next, &card->devices, list) + __snd_device_free(dev); } diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c new file mode 100644 index 00000000000..886be7da989 --- /dev/null +++ b/sound/core/hrtimer.c @@ -0,0 +1,166 @@ +/* + * ALSA timer back-end using hrtimer + * Copyright (C) 2008 Takashi Iwai + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/hrtimer.h> +#include <sound/core.h> +#include <sound/timer.h> + +MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); +MODULE_DESCRIPTION("ALSA hrtimer backend"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER)); + +#define NANO_SEC 1000000000UL /* 10^9 in sec */ +static unsigned int resolution; + +struct snd_hrtimer { + struct snd_timer *timer; + struct hrtimer hrt; + atomic_t running; +}; + +static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) +{ + struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); + struct snd_timer *t = stime->timer; + unsigned long oruns; + + if (!atomic_read(&stime->running)) + return HRTIMER_NORESTART; + + oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); + snd_timer_interrupt(stime->timer, t->sticks * oruns); + + if (!atomic_read(&stime->running)) + return HRTIMER_NORESTART; + return HRTIMER_RESTART; +} + +static int snd_hrtimer_open(struct snd_timer *t) +{ + struct snd_hrtimer *stime; + + stime = kmalloc(sizeof(*stime), GFP_KERNEL); + if (!stime) + return -ENOMEM; + hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + stime->timer = t; + stime->hrt.function = snd_hrtimer_callback; + atomic_set(&stime->running, 0); + t->private_data = stime; + return 0; +} + +static int snd_hrtimer_close(struct snd_timer *t) +{ + struct snd_hrtimer *stime = t->private_data; + + if (stime) { + hrtimer_cancel(&stime->hrt); + kfree(stime); + t->private_data = NULL; + } + return 0; +} + +static int snd_hrtimer_start(struct snd_timer *t) +{ + struct snd_hrtimer *stime = t->private_data; + + atomic_set(&stime->running, 0); + hrtimer_cancel(&stime->hrt); + hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), + HRTIMER_MODE_REL); + atomic_set(&stime->running, 1); + return 0; +} + +static int snd_hrtimer_stop(struct snd_timer *t) +{ + struct snd_hrtimer *stime = t->private_data; + atomic_set(&stime->running, 0); + return 0; +} + +static struct snd_timer_hardware hrtimer_hw = { + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, + .open = snd_hrtimer_open, + .close = snd_hrtimer_close, + .start = snd_hrtimer_start, + .stop = snd_hrtimer_stop, +}; + +/* + * entry functions + */ + +static struct snd_timer *mytimer; + +static int __init snd_hrtimer_init(void) +{ + struct snd_timer *timer; + struct timespec tp; + int err; + + hrtimer_get_res(CLOCK_MONOTONIC, &tp); + if (tp.tv_sec > 0 || !tp.tv_nsec) { + pr_err("snd-hrtimer: Invalid resolution %u.%09u", + (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec); + return -EINVAL; + } + resolution = tp.tv_nsec; + + /* Create a new timer and set up the fields */ + err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER, + &timer); + if (err < 0) + return err; + + timer->module = THIS_MODULE; + strcpy(timer->name, "HR timer"); + timer->hw = hrtimer_hw; + timer->hw.resolution = resolution; + timer->hw.ticks = NANO_SEC / resolution; + + err = snd_timer_global_register(timer); + if (err < 0) { + snd_timer_global_free(timer); + return err; + } + mytimer = timer; /* remember this */ + + return 0; +} + +static void __exit snd_hrtimer_exit(void) +{ + if (mytimer) { + snd_timer_global_free(mytimer); + mytimer = NULL; + } +} + +module_init(snd_hrtimer_init); +module_exit(snd_hrtimer_exit); diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 6d6589f9389..69459e5f712 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/control.h> #include <sound/minors.h> @@ -99,11 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) if (hw == NULL) return -ENODEV; - if (!hw->ops.open) - return -ENXIO; - - if (!try_module_get(hw->card->module)) + if (!try_module_get(hw->card->module)) { + snd_card_unref(hw->card); return -EFAULT; + } init_waitqueue_entry(&wait, current); add_wait_queue(&hw->open_wait, &wait); @@ -113,6 +113,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) err = -EBUSY; break; } + if (!hw->ops.open) { + err = 0; + break; + } err = hw->ops.open(hw, file); if (err >= 0) break; @@ -127,6 +131,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) mutex_unlock(&hw->open_mutex); schedule(); mutex_lock(&hw->open_mutex); + if (hw->card->shutdown) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; @@ -146,12 +154,13 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) mutex_unlock(&hw->open_mutex); if (err < 0) module_put(hw->card->module); + snd_card_unref(hw->card); return err; } static int snd_hwdep_release(struct inode *inode, struct file * file) { - int err = -ENXIO; + int err = 0; struct snd_hwdep *hw = file->private_data; struct module *mod = hw->card->module; @@ -271,7 +280,14 @@ static int snd_hwdep_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)arg)) return -EFAULT; mutex_lock(®ister_mutex); - device = device < 0 ? 0 : device + 1; + + if (device < 0) + device = 0; + else if (device < SNDRV_MINOR_HWDEPS) + device++; + else + device = SNDRV_MINOR_HWDEPS; + while (device < SNDRV_MINOR_HWDEPS) { if (snd_hwdep_search(card, device)) break; @@ -340,7 +356,7 @@ static const struct file_operations snd_hwdep_f_ops = * The callbacks (hwdep->ops) must be set on the returned instance * after this call manually by the caller. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_hwdep_new(struct snd_card *card, char *id, int device, struct snd_hwdep **rhwdep) @@ -353,12 +369,13 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, .dev_disconnect = snd_hwdep_dev_disconnect, }; - snd_assert(rhwdep != NULL, return -EINVAL); - *rhwdep = NULL; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; + if (rhwdep) + *rhwdep = NULL; hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); if (hwdep == NULL) { - snd_printk(KERN_ERR "hwdep: cannot allocate\n"); + dev_err(card->dev, "hwdep: cannot allocate\n"); return -ENOMEM; } hwdep->card = card; @@ -374,13 +391,16 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, } init_waitqueue_head(&hwdep->open_wait); mutex_init(&hwdep->open_mutex); - *rhwdep = hwdep; + if (rhwdep) + *rhwdep = hwdep; return 0; } +EXPORT_SYMBOL(snd_hwdep_new); static int snd_hwdep_free(struct snd_hwdep *hwdep) { - snd_assert(hwdep != NULL, return -ENXIO); + if (!hwdep) + return 0; if (hwdep->private_free) hwdep->private_free(hwdep); kfree(hwdep); @@ -396,37 +416,61 @@ static int snd_hwdep_dev_free(struct snd_device *device) static int snd_hwdep_dev_register(struct snd_device *device) { struct snd_hwdep *hwdep = device->device_data; + struct snd_card *card = hwdep->card; + struct device *dev; int err; char name[32]; mutex_lock(®ister_mutex); - if (snd_hwdep_search(hwdep->card, hwdep->device)) { + if (snd_hwdep_search(card, hwdep->device)) { mutex_unlock(®ister_mutex); return -EBUSY; } list_add_tail(&hwdep->list, &snd_hwdep_devices); sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device); - if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, - hwdep->card, hwdep->device, - &snd_hwdep_f_ops, hwdep, name)) < 0) { - snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n", - hwdep->card->number, hwdep->device); + dev = hwdep->dev; + if (!dev) + dev = snd_card_get_device_link(hwdep->card); + err = snd_register_device_for_dev(SNDRV_DEVICE_TYPE_HWDEP, + hwdep->card, hwdep->device, + &snd_hwdep_f_ops, hwdep, name, dev); + if (err < 0) { + dev_err(dev, + "unable to register hardware dependent device %i:%i\n", + card->number, hwdep->device); list_del(&hwdep->list); mutex_unlock(®ister_mutex); return err; } + + if (hwdep->groups) { + struct device *d = snd_get_device(SNDRV_DEVICE_TYPE_HWDEP, + hwdep->card, hwdep->device); + if (d) { + if (hwdep->private_data) + dev_set_drvdata(d, hwdep->private_data); + err = sysfs_create_groups(&d->kobj, hwdep->groups); + if (err < 0) + dev_warn(dev, + "hwdep %d:%d: cannot create sysfs groups\n", + card->number, hwdep->device); + put_device(d); + } + } + #ifdef CONFIG_SND_OSSEMUL hwdep->ossreg = 0; if (hwdep->oss_type >= 0) { if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { - snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n"); + dev_warn(dev, + "only hwdep device 0 can be registered as OSS direct FM device!\n"); } else { if (snd_register_oss_device(hwdep->oss_type, - hwdep->card, hwdep->device, - &snd_hwdep_f_ops, hwdep, - hwdep->oss_dev) < 0) { - snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n", - hwdep->card->number, hwdep->device); + card, hwdep->device, + &snd_hwdep_f_ops, hwdep) < 0) { + dev_err(dev, + "unable to register OSS compatibility device %i:%i\n", + card->number, hwdep->device); } else hwdep->ossreg = 1; } @@ -440,18 +484,22 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) { struct snd_hwdep *hwdep = device->device_data; - snd_assert(hwdep != NULL, return -ENXIO); + if (snd_BUG_ON(!hwdep)) + return -ENXIO; mutex_lock(®ister_mutex); if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) { mutex_unlock(®ister_mutex); return -EINVAL; } + mutex_lock(&hwdep->open_mutex); + wake_up(&hwdep->open_wait); #ifdef CONFIG_SND_OSSEMUL if (hwdep->ossreg) snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); #endif snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); list_del_init(&hwdep->list); + mutex_unlock(&hwdep->open_mutex); mutex_unlock(®ister_mutex); return 0; } @@ -520,5 +568,3 @@ static void __exit alsa_hwdep_exit(void) module_init(alsa_hwdep_init) module_exit(alsa_hwdep_exit) - -EXPORT_SYMBOL(snd_hwdep_new); diff --git a/sound/core/info.c b/sound/core/info.c index 9977ec2eace..051d55b0552 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -21,12 +21,14 @@ #include <linux/init.h> #include <linux/time.h> -#include <linux/smp_lock.h> +#include <linux/mm.h> +#include <linux/slab.h> #include <linux/string.h> +#include <linux/module.h> #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> @@ -87,12 +89,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, char *nbuf; nsize = PAGE_ALIGN(nsize); - nbuf = kmalloc(nsize, GFP_KERNEL); + nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO); if (! nbuf) return -ENOMEM; - memcpy(nbuf, buffer->buffer, buffer->len); - kfree(buffer->buffer); buffer->buffer = nbuf; buffer->len = nsize; return 0; @@ -105,9 +105,9 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, * * Outputs the string on the procfs buffer just like printf(). * - * Returns the size of output string. + * Return: The size of output string, or a negative error code. */ -int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...) +int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) { va_list args; int len, res; @@ -153,56 +153,48 @@ EXPORT_SYMBOL(snd_seq_root); struct snd_info_entry *snd_oss_root; #endif -static inline void snd_info_entry_prepare(struct proc_dir_entry *de) -{ - de->owner = THIS_MODULE; -} - -static void snd_remove_proc_entry(struct proc_dir_entry *parent, - struct proc_dir_entry *de) -{ - if (de) - remove_proc_entry(de->name, parent); -} - static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) { struct snd_info_private_data *data; struct snd_info_entry *entry; - loff_t ret; + loff_t ret = -EINVAL, size; data = file->private_data; entry = data->entry; - lock_kernel(); - switch (entry->content) { - case SNDRV_INFO_CONTENT_TEXT: - switch (orig) { - case SEEK_SET: - file->f_pos = offset; - ret = file->f_pos; - goto out; - case SEEK_CUR: - file->f_pos += offset; - ret = file->f_pos; - goto out; - case SEEK_END: - default: - ret = -EINVAL; - goto out; - } + mutex_lock(&entry->access); + if (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->llseek) { + offset = entry->c.ops->llseek(entry, + data->file_private_data, + file, offset, orig); + goto out; + } + if (entry->content == SNDRV_INFO_CONTENT_DATA) + size = entry->size; + else + size = 0; + switch (orig) { + case SEEK_SET: break; - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->llseek) { - ret = entry->c.ops->llseek(entry, - data->file_private_data, - file, offset, orig); + case SEEK_CUR: + offset += file->f_pos; + break; + case SEEK_END: + if (!size) goto out; - } + offset += size; break; - } - ret = -ENXIO; -out: - unlock_kernel(); + default: + goto out; + } + if (offset < 0) + goto out; + if (size && offset > size) + offset = size; + file->f_pos = offset; + ret = offset; + out: + mutex_unlock(&entry->access); return ret; } @@ -216,7 +208,8 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, loff_t pos; data = file->private_data; - snd_assert(data != NULL, return -ENXIO); + if (snd_BUG_ON(!data)) + return -ENXIO; pos = *offset; if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) return -EIO; @@ -236,10 +229,15 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, return -EFAULT; break; case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->read) + if (pos >= entry->size) + return 0; + if (entry->c.ops->read) { + size = entry->size - pos; + size = min(count, size); size = entry->c.ops->read(entry, data->file_private_data, - file, buffer, count, pos); + file, buffer, size, pos); + } break; } if ((ssize_t) size > 0) @@ -257,7 +255,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer loff_t pos; data = file->private_data; - snd_assert(data != NULL, return -ENXIO); + if (snd_BUG_ON(!data)) + return -ENXIO; entry = data->entry; pos = *offset; if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) @@ -285,10 +284,13 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer size = count; break; case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->write) + if (entry->c.ops->write && count > 0) { + size_t maxsize = entry->size - pos; + count = min(count, maxsize); size = entry->c.ops->write(entry, data->file_private_data, file, buffer, count, pos); + } break; } if ((ssize_t) size > 0) @@ -301,12 +303,10 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) struct snd_info_entry *entry; struct snd_info_private_data *data; struct snd_info_buffer *buffer; - struct proc_dir_entry *p; int mode, err; mutex_lock(&info_mutex); - p = PDE(inode); - entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; + entry = PDE_DATA(inode); if (entry == NULL || ! entry->p) { mutex_unlock(&info_mutex); return -ENODEV; @@ -344,7 +344,7 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) goto __nomem; data->rbuffer = buffer; buffer->len = PAGE_SIZE; - buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); + buffer->buffer = kzalloc(buffer->len, GFP_KERNEL); if (buffer->buffer == NULL) goto __nomem; } @@ -418,9 +418,14 @@ static int snd_info_entry_release(struct inode *inode, struct file *file) if (entry->c.text.write) { entry->c.text.write(entry, data->wbuffer); if (data->wbuffer->error) { - snd_printk(KERN_WARNING "data write error to %s (%i)\n", - entry->name, - data->wbuffer->error); + if (entry->card) + dev_warn(entry->card->dev, "info: data write error to %s (%i)\n", + entry->name, + data->wbuffer->error); + else + pr_warn("ALSA: info: data write error to %s (%i)\n", + entry->name, + data->wbuffer->error); } } kfree(data->wbuffer->buffer); @@ -487,7 +492,7 @@ static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct snd_info_private_data *data; struct snd_info_entry *entry; @@ -519,32 +524,11 @@ static const struct file_operations snd_info_entry_operations = .release = snd_info_entry_release, }; -/** - * snd_create_proc_entry - create a procfs entry - * @name: the name of the proc file - * @mode: the file permission bits, S_Ixxx - * @parent: the parent proc-directory entry - * - * Creates a new proc file entry with the given name and permission - * on the given directory. - * - * Returns the pointer of new instance or NULL on failure. - */ -static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *p; - p = create_proc_entry(name, mode, parent); - if (p) - snd_info_entry_prepare(p); - return p; -} - int __init snd_info_init(void) { struct proc_dir_entry *p; - p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); + p = proc_mkdir("asound", NULL); if (p == NULL) return -ENOMEM; snd_proc_root = p; @@ -561,7 +545,7 @@ int __init snd_info_init(void) snd_oss_root = entry; } #endif -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) { struct snd_info_entry *entry; if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) @@ -588,13 +572,13 @@ int __exit snd_info_done(void) snd_minor_info_done(); snd_info_version_done(); if (snd_proc_root) { -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) snd_info_free_entry(snd_seq_root); #endif #ifdef CONFIG_SND_OSSEMUL snd_info_free_entry(snd_oss_root); #endif - snd_remove_proc_entry(&proc_root, snd_proc_root); + proc_remove(snd_proc_root); } return 0; } @@ -613,7 +597,8 @@ int snd_info_card_create(struct snd_card *card) char str[8]; struct snd_info_entry *entry; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; sprintf(str, "card%i", card->number); if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) @@ -635,7 +620,8 @@ int snd_info_card_register(struct snd_card *card) { struct proc_dir_entry *p; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; if (!strcmp(card->id, card->proc_root->name)) return 0; @@ -648,17 +634,33 @@ int snd_info_card_register(struct snd_card *card) } /* - * de-register the card proc file - * called from init.c + * called on card->id change */ -void snd_info_card_disconnect(struct snd_card *card) +void snd_info_card_id_change(struct snd_card *card) { - snd_assert(card != NULL, return); mutex_lock(&info_mutex); if (card->proc_root_link) { - snd_remove_proc_entry(snd_proc_root, card->proc_root_link); + proc_remove(card->proc_root_link); card->proc_root_link = NULL; } + if (strcmp(card->id, card->proc_root->name)) + card->proc_root_link = proc_symlink(card->id, + snd_proc_root, + card->proc_root->name); + mutex_unlock(&info_mutex); +} + +/* + * de-register the card proc file + * called from init.c + */ +void snd_info_card_disconnect(struct snd_card *card) +{ + if (!card) + return; + mutex_lock(&info_mutex); + proc_remove(card->proc_root_link); + card->proc_root_link = NULL; if (card->proc_root) snd_info_disconnect(card->proc_root); mutex_unlock(&info_mutex); @@ -670,7 +672,8 @@ void snd_info_card_disconnect(struct snd_card *card) */ int snd_info_card_free(struct snd_card *card) { - snd_assert(card != NULL, return -ENXIO); + if (!card) + return 0; snd_info_free_entry(card->proc_root); card->proc_root = NULL; return 0; @@ -685,32 +688,27 @@ int snd_info_card_free(struct snd_card *card) * * Reads one line from the buffer and stores the string. * - * Returns zero if successful, or 1 if error or EOF. + * Return: Zero if successful, or 1 if error or EOF. */ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { int c = -1; + if (snd_BUG_ON(!buffer || !buffer->buffer)) + return 1; if (len <= 0 || buffer->stop || buffer->error) return 1; - while (--len > 0) { + while (!buffer->stop) { c = buffer->buffer[buffer->curr++]; - if (c == '\n') { - if (buffer->curr >= buffer->size) - buffer->stop = 1; - break; - } - *line++ = c; - if (buffer->curr >= buffer->size) { + if (buffer->curr >= buffer->size) buffer->stop = 1; + if (c == '\n') break; + if (len) { + len--; + *line++ = c; } } - while (c != '\n' && !buffer->stop) { - c = buffer->buffer[buffer->curr++]; - if (buffer->curr >= buffer->size) - buffer->stop = 1; - } *line = '\0'; return 0; } @@ -726,10 +724,10 @@ EXPORT_SYMBOL(snd_info_get_line); * Parses the original string and copy a token to the given * string buffer. * - * Returns the updated pointer of the original string so that + * Return: The updated pointer of the original string so that * it can be used for the next call. */ -char *snd_info_get_str(char *dest, char *src, int len) +const char *snd_info_get_str(char *dest, const char *src, int len) { int c; @@ -765,7 +763,7 @@ EXPORT_SYMBOL(snd_info_get_str); * Usually called from other functions such as * snd_info_create_card_entry(). * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ static struct snd_info_entry *snd_info_create_entry(const char *name) { @@ -794,7 +792,7 @@ static struct snd_info_entry *snd_info_create_entry(const char *name) * * Creates a new info entry and assigns it to the given module. * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, @@ -818,7 +816,7 @@ EXPORT_SYMBOL(snd_info_create_module_entry); * * Creates a new info entry and assigns it to the given card. * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, @@ -848,8 +846,8 @@ static void snd_info_disconnect(struct snd_info_entry *entry) return; list_del_init(&entry->list); root = entry->parent == NULL ? snd_proc_root : entry->parent->p; - snd_assert(root, return); - snd_remove_proc_entry(root, entry->p); + snd_BUG_ON(!root); + proc_remove(entry->p); entry->p = NULL; } @@ -884,7 +882,7 @@ static int snd_info_dev_register_entry(struct snd_device *device) * For releasing this entry, use snd_device_free() instead of * snd_info_free_entry(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp) @@ -940,25 +938,31 @@ EXPORT_SYMBOL(snd_info_free_entry); * * Registers the proc info entry. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_info_register(struct snd_info_entry * entry) { struct proc_dir_entry *root, *p = NULL; - snd_assert(entry != NULL, return -ENXIO); + if (snd_BUG_ON(!entry)) + return -ENXIO; root = entry->parent == NULL ? snd_proc_root : entry->parent->p; mutex_lock(&info_mutex); - p = snd_create_proc_entry(entry->name, entry->mode, root); - if (!p) { - mutex_unlock(&info_mutex); - return -ENOMEM; + if (S_ISDIR(entry->mode)) { + p = proc_mkdir_mode(entry->name, entry->mode, root); + if (!p) { + mutex_unlock(&info_mutex); + return -ENOMEM; + } + } else { + p = proc_create_data(entry->name, entry->mode, root, + &snd_info_entry_operations, entry); + if (!p) { + mutex_unlock(&info_mutex); + return -ENOMEM; + } + proc_set_size(p, entry->size); } - p->owner = entry->module; - if (!S_ISDIR(entry->mode)) - p->proc_fops = &snd_info_entry_operations; - p->size = entry->size; - p->data = entry; entry->p = p; if (entry->parent) list_add_tail(&entry->list, &entry->parent->children); @@ -977,9 +981,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 e35789a9275..83c29dbff9c 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -22,10 +22,10 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/string.h> +#include <linux/export.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> -#include <sound/version.h> #include <linux/utsname.h> #include <linux/mutex.h> @@ -43,8 +43,10 @@ int snd_oss_info_register(int dev, int num, char *string) { char *x; - snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); - snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); + if (snd_BUG_ON(dev < 0 || dev >= SNDRV_OSS_INFO_DEV_COUNT)) + return -ENXIO; + if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS)) + return -ENXIO; mutex_lock(&strings); if (string == NULL) { if ((x = snd_sndstat_strings[num][dev]) != NULL) { @@ -91,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/init.c b/sound/core/init.c index e3338d6071e..7bdfd19e24a 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -21,22 +21,34 @@ #include <linux/init.h> #include <linux/sched.h> +#include <linux/module.h> +#include <linux/device.h> #include <linux/file.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/ctype.h> #include <linux/pm.h> +#include <linux/completion.h> #include <sound/core.h> #include <sound/control.h> #include <sound/info.h> +/* monitor files for graceful shutdown (hotplug) */ +struct snd_monitor_file { + struct file *file; + const struct file_operations *disconnected_f_op; + struct list_head shutdown_list; /* still need to shutdown */ + struct list_head list; /* link of monitor files */ +}; + static DEFINE_SPINLOCK(shutdown_lock); static LIST_HEAD(shutdown_files); static const struct file_operations snd_shutdown_f_ops; -static unsigned int snd_cards_lock; /* locked for registering/using */ +/* locked for registering/using */ +static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS); struct snd_card *snd_cards[SNDRV_CARDS]; EXPORT_SYMBOL(snd_cards); @@ -46,17 +58,24 @@ static char *slots[SNDRV_CARDS]; module_param_array(slots, charp, NULL, 0444); MODULE_PARM_DESC(slots, "Module names assigned to the slots."); -/* return non-zero if the given index is already reserved for another +/* return non-zero if the given index is reserved for the given * module via slots option */ -static int module_slot_mismatch(struct module *module, int idx) +static int module_slot_match(struct module *module, int idx) { + int match = 1; #ifdef MODULE - char *s1, *s2; - if (!module || !module->name || !slots[idx]) + const char *s1, *s2; + + if (!module || !*module->name || !slots[idx]) return 0; - s1 = slots[idx]; - s2 = module->name; + + s1 = module->name; + s2 = slots[idx]; + if (*s2 == '!') { + match = 0; /* negative match */ + s2++; + } /* compare module name strings * hyphens are handled as equivalent with underscore */ @@ -68,15 +87,15 @@ static int module_slot_mismatch(struct module *module, int idx) if (c2 == '-') c2 = '_'; if (c1 != c2) - return 1; + return !match; if (!c1) break; } -#endif - return 0; +#endif /* MODULE */ + return match; } -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); EXPORT_SYMBOL(snd_mixer_oss_notify_callback); #endif @@ -94,11 +113,11 @@ static inline int init_info_for_card(struct snd_card *card) struct snd_info_entry *entry; if ((err = snd_info_card_register(card)) < 0) { - snd_printd("unable to create card info\n"); + dev_dbg(card->dev, "unable to create card info\n"); return err; } if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { - snd_printd("unable to create card entry\n"); + dev_dbg(card->dev, "unable to create card entry\n"); return err; } entry->c.text.read = snd_card_id_read; @@ -113,101 +132,146 @@ static inline int init_info_for_card(struct snd_card *card) #define init_info_for_card(card) #endif +static int check_empty_slot(struct module *module, int slot) +{ + return !slots[slot] || !*slots[slot]; +} + +/* return an empty slot number (>= 0) found in the given bitmask @mask. + * @mask == -1 == 0xffffffff means: take any free slot up to 32 + * when no slot is available, return the original @mask as is. + */ +static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), + struct module *module) +{ + int slot; + + for (slot = 0; slot < SNDRV_CARDS; slot++) { + if (slot < 32 && !(mask & (1U << slot))) + continue; + if (!test_bit(slot, snd_cards_lock)) { + if (check(module, slot)) + return slot; /* found */ + } + } + return mask; /* unchanged */ +} + +static int snd_card_do_free(struct snd_card *card); +static const struct attribute_group *card_dev_attr_groups[]; + +static void release_card_device(struct device *dev) +{ + snd_card_do_free(dev_to_snd_card(dev)); +} + /** * snd_card_new - create and initialize a soundcard structure + * @parent: the parent device object * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] * @xid: card identification (ASCII string) * @module: top level module for locking * @extra_size: allocate this extra size after the main soundcard structure + * @card_ret: the pointer to store the created card instance * * Creates and initializes a soundcard structure. * - * Returns kmallocated snd_card structure. Creates the ALSA control interface - * (which is blocked until snd_card_register function is called). + * The function allocates snd_card instance via kzalloc with the given + * space for the driver to use freely. The allocated struct is stored + * in the given card_ret pointer. + * + * Return: Zero if successful or a negative error code. */ -struct snd_card *snd_card_new(int idx, const char *xid, - struct module *module, int extra_size) +int snd_card_new(struct device *parent, int idx, const char *xid, + struct module *module, int extra_size, + struct snd_card **card_ret) { struct snd_card *card; int err; + if (snd_BUG_ON(!card_ret)) + return -EINVAL; + *card_ret = NULL; + if (extra_size < 0) extra_size = 0; card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); - if (card == NULL) - return NULL; - if (xid) { - if (!snd_info_check_reserved_words(xid)) - goto __error; + if (!card) + return -ENOMEM; + if (extra_size > 0) + card->private_data = (char *)card + sizeof(struct snd_card); + if (xid) strlcpy(card->id, xid, sizeof(card->id)); - } err = 0; mutex_lock(&snd_card_mutex); - if (idx < 0) { - int idx2; - for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) - /* idx == -1 == 0xffff means: take any free slot */ - if (~snd_cards_lock & idx & 1<<idx2) { - if (module_slot_mismatch(module, idx2)) - continue; - idx = idx2; - if (idx >= snd_ecards_limit) - snd_ecards_limit = idx + 1; - break; - } - } else { - if (idx < snd_ecards_limit) { - if (snd_cards_lock & (1 << idx)) - err = -EBUSY; /* invalid */ - } else { - if (idx < SNDRV_CARDS) - snd_ecards_limit = idx + 1; /* increase the limit */ - else - err = -ENODEV; - } - } - if (idx < 0 || err < 0) { + if (idx < 0) /* first check the matching module-name slot */ + idx = get_slot_from_bitmask(idx, module_slot_match, module); + if (idx < 0) /* if not matched, assign an empty slot */ + idx = get_slot_from_bitmask(idx, check_empty_slot, module); + if (idx < 0) + err = -ENODEV; + else if (idx < snd_ecards_limit) { + if (test_bit(idx, snd_cards_lock)) + err = -EBUSY; /* invalid */ + } else if (idx >= SNDRV_CARDS) + err = -ENODEV; + if (err < 0) { mutex_unlock(&snd_card_mutex); - snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n", + dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", idx, snd_ecards_limit - 1, err); - goto __error; + kfree(card); + return err; } - snd_cards_lock |= 1 << idx; /* lock it */ + set_bit(idx, snd_cards_lock); /* lock it */ + if (idx >= snd_ecards_limit) + snd_ecards_limit = idx + 1; /* increase the limit */ mutex_unlock(&snd_card_mutex); + card->dev = parent; card->number = idx; card->module = module; INIT_LIST_HEAD(&card->devices); init_rwsem(&card->controls_rwsem); rwlock_init(&card->ctl_files_rwlock); + mutex_init(&card->user_ctl_lock); INIT_LIST_HEAD(&card->controls); INIT_LIST_HEAD(&card->ctl_files); spin_lock_init(&card->files_lock); - init_waitqueue_head(&card->shutdown_sleep); + INIT_LIST_HEAD(&card->files_list); #ifdef CONFIG_PM mutex_init(&card->power_lock); init_waitqueue_head(&card->power_sleep); #endif + + device_initialize(&card->card_dev); + card->card_dev.parent = parent; + card->card_dev.class = sound_class; + card->card_dev.release = release_card_device; + card->card_dev.groups = card_dev_attr_groups; + err = kobject_set_name(&card->card_dev.kobj, "card%d", idx); + if (err < 0) + goto __error; + /* the control interface cannot be accessed from the user space until */ /* snd_cards_bitmask and snd_cards are set with snd_card_register */ - if ((err = snd_ctl_create(card)) < 0) { - snd_printd("unable to register control minors\n"); + err = snd_ctl_create(card); + if (err < 0) { + dev_err(parent, "unable to register control minors\n"); goto __error; } - if ((err = snd_info_card_create(card)) < 0) { - snd_printd("unable to create card info\n"); + err = snd_info_card_create(card); + if (err < 0) { + dev_err(parent, "unable to create card info\n"); goto __error_ctl; } - if (extra_size > 0) - card->private_data = (char *)card + sizeof(struct snd_card); - return card; + *card_ret = card; + return 0; __error_ctl: - snd_device_free_all(card, SNDRV_DEV_CMD_PRE); + snd_device_free_all(card); __error: - kfree(card); - return NULL; + put_device(&card->card_dev); + return err; } - EXPORT_SYMBOL(snd_card_new); /* return non-zero if a card is already locked */ @@ -216,7 +280,7 @@ int snd_card_locked(int card) int locked; mutex_lock(&snd_card_mutex); - locked = snd_cards_lock & (1 << card); + locked = test_bit(card, snd_cards_lock); mutex_unlock(&snd_card_mutex); return locked; } @@ -246,15 +310,19 @@ static int snd_disconnect_release(struct inode *inode, struct file *file) list_for_each_entry(_df, &shutdown_files, shutdown_list) { if (_df->file == file) { df = _df; + list_del_init(&df->shutdown_list); break; } } spin_unlock(&shutdown_lock); - if (likely(df)) + if (likely(df)) { + if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) + df->disconnected_f_op->fasync(-1, file, 0); return df->disconnected_f_op->release(inode, file); + } - panic("%s(%p, %p) failed!", __FUNCTION__, inode, file); + panic("%s(%p, %p) failed!", __func__, inode, file); } static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) @@ -300,7 +368,7 @@ static const struct file_operations snd_shutdown_f_ops = * * Disconnects all APIs from the file-operations (user space). * - * Returns zero, otherwise a negative error code. + * Return: Zero, otherwise a negative error code. * * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). @@ -308,9 +376,11 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - struct file *file; int err; + if (!card) + return -EINVAL; + spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); @@ -322,15 +392,13 @@ int snd_card_disconnect(struct snd_card *card) /* phase 1: disable fops (user space) operations for ALSA API */ mutex_lock(&snd_card_mutex); snd_cards[card->number] = NULL; + clear_bit(card->number, snd_cards_lock); mutex_unlock(&snd_card_mutex); /* phase 2: replace file->f_op with special dummy operations */ spin_lock(&card->files_lock); - mfile = card->files; - while (mfile) { - file = mfile->file; - + list_for_each_entry(mfile, &card->files_list, list) { /* it's critical part, use endless loop */ /* we have no room to fail */ mfile->disconnected_f_op = mfile->file->f_op; @@ -341,15 +409,13 @@ int snd_card_disconnect(struct snd_card *card) mfile->file->f_op = &snd_shutdown_f_ops; fops_get(mfile->file->f_op); - - mfile = mfile->next; } spin_unlock(&card->files_lock); /* phase 3: notify all connected devices about disconnection */ /* at this point, they cannot respond to any calls except release() */ -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); #endif @@ -357,9 +423,16 @@ int snd_card_disconnect(struct snd_card *card) /* notify all devices that we are disconnected */ err = snd_device_disconnect_all(card); if (err < 0) - snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); + dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); snd_info_card_disconnect(card); + if (card->registered) { + device_del(&card->card_dev); + card->registered = false; + } +#ifdef CONFIG_PM + wake_up(&card->power_sleep); +#endif return 0; } @@ -373,156 +446,235 @@ EXPORT_SYMBOL(snd_card_disconnect); * devices automatically. That is, you don't have to release the devices * by yourself. * - * Returns zero. Frees all associated devices and frees the control + * Return: Zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ static int snd_card_do_free(struct snd_card *card) { -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); #endif - if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { - snd_printk(KERN_ERR "unable to free all devices (pre)\n"); - /* Fatal, but this situation should never occur */ - } - if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { - snd_printk(KERN_ERR "unable to free all devices (normal)\n"); - /* Fatal, but this situation should never occur */ - } - if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { - snd_printk(KERN_ERR "unable to free all devices (post)\n"); - /* Fatal, but this situation should never occur */ - } + snd_device_free_all(card); if (card->private_free) card->private_free(card); snd_info_free_entry(card->proc_id); if (snd_info_card_free(card) < 0) { - snd_printk(KERN_WARNING "unable to free card info\n"); + dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ } -#ifndef CONFIG_SYSFS_DEPRECATED - if (card->card_dev) - device_unregister(card->card_dev); -#endif + if (card->release_completion) + complete(card->release_completion); kfree(card); return 0; } -static int snd_card_free_prepare(struct snd_card *card) -{ - if (card == NULL) - return -EINVAL; - (void) snd_card_disconnect(card); - mutex_lock(&snd_card_mutex); - snd_cards[card->number] = NULL; - snd_cards_lock &= ~(1 << card->number); - mutex_unlock(&snd_card_mutex); -#ifdef CONFIG_PM - wake_up(&card->power_sleep); -#endif - return 0; -} - int snd_card_free_when_closed(struct snd_card *card) { - int free_now = 0; - int ret = snd_card_free_prepare(card); + int ret = snd_card_disconnect(card); if (ret) return ret; - - spin_lock(&card->files_lock); - if (card->files == NULL) - free_now = 1; - else - card->free_on_last_close = 1; - spin_unlock(&card->files_lock); - - if (free_now) - snd_card_do_free(card); + put_device(&card->card_dev); return 0; } - EXPORT_SYMBOL(snd_card_free_when_closed); int snd_card_free(struct snd_card *card) { - int ret = snd_card_free_prepare(card); + struct completion released; + int ret; + + init_completion(&released); + card->release_completion = &released; + ret = snd_card_free_when_closed(card); if (ret) return ret; - /* wait, until all devices are ready for the free operation */ - wait_event(card->shutdown_sleep, card->files == NULL); - snd_card_do_free(card); + wait_for_completion(&released); return 0; } - EXPORT_SYMBOL(snd_card_free); -static void choose_default_id(struct snd_card *card) +/* retrieve the last word of shortname or longname */ +static const char *retrieve_id_from_card_name(const char *name) { - int i, len, idx_flag = 0, loops = SNDRV_CARDS; - char *id, *spos; - - id = spos = card->shortname; - while (*id != '\0') { - if (*id == ' ') - spos = id + 1; - id++; + const char *spos = name; + + while (*name) { + if (isspace(*name) && isalnum(name[1])) + spos = name + 1; + name++; } + return spos; +} + +/* return true if the given id string doesn't conflict any other card ids */ +static bool card_id_ok(struct snd_card *card, const char *id) +{ + int i; + if (!snd_info_check_reserved_words(id)) + return false; + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && snd_cards[i] != card && + !strcmp(snd_cards[i]->id, id)) + return false; + } + return true; +} + +/* copy to card->id only with valid letters from nid */ +static void copy_valid_id_string(struct snd_card *card, const char *src, + const char *nid) +{ + char *id = card->id; + + while (*nid && !isalnum(*nid)) + nid++; + if (isdigit(*nid)) + *id++ = isalpha(*src) ? *src : 'D'; + while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { + if (isalnum(*nid)) + *id++ = *nid; + nid++; + } + *id = 0; +} + +/* Set card->id from the given string + * If the string conflicts with other ids, add a suffix to make it unique. + */ +static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, + const char *nid) +{ + int len, loops; + bool is_default = false; + char *id; + + copy_valid_id_string(card, src, nid); id = card->id; - while (*spos != '\0' && !isalnum(*spos)) - spos++; - if (isdigit(*spos)) - *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D'; - while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { - if (isalnum(*spos)) - *id++ = *spos; - spos++; + + again: + /* use "Default" for obviously invalid strings + * ("card" conflicts with proc directories) + */ + if (!*id || !strncmp(id, "card", 4)) { + strcpy(id, "Default"); + is_default = true; } - *id = '\0'; - id = card->id; - - if (*id == '\0') - strcpy(id, "default"); + len = strlen(id); + for (loops = 0; loops < SNDRV_CARDS; loops++) { + char *spos; + char sfxstr[5]; /* "_012" */ + int sfxlen; - while (1) { - if (loops-- == 0) { - snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id); - strcpy(card->id, card->proc_root->name); - return; - } - if (!snd_info_check_reserved_words(id)) - goto __change; - for (i = 0; i < snd_ecards_limit; i++) { - if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) - goto __change; - } - break; - - __change: - len = strlen(id); - if (idx_flag) { - if (id[len-1] != '9') - id[len-1]++; - else - id[len-1] = 'A'; - } else if ((size_t)len <= sizeof(card->id) - 3) { - strcat(id, "_1"); - idx_flag++; - } else { - spos = id + len - 2; - if ((size_t)len <= sizeof(card->id) - 2) - spos++; - *spos++ = '_'; - *spos++ = '1'; - *spos++ = '\0'; - idx_flag++; - } + if (card_id_ok(card, id)) + return; /* OK */ + + /* Add _XYZ suffix */ + sprintf(sfxstr, "_%X", loops + 1); + sfxlen = strlen(sfxstr); + if (len + sfxlen >= sizeof(card->id)) + spos = id + sizeof(card->id) - sfxlen - 1; + else + spos = id + len; + strcpy(spos, sfxstr); + } + /* fallback to the default id */ + if (!is_default) { + *id = 0; + goto again; + } + /* last resort... */ + dev_err(card->dev, "unable to set card id (%s)\n", id); + if (card->proc_root->name) + strlcpy(card->id, card->proc_root->name, sizeof(card->id)); +} + +/** + * snd_card_set_id - set card identification name + * @card: soundcard structure + * @nid: new identification string + * + * This function sets the card identification and checks for name + * collisions. + */ +void snd_card_set_id(struct snd_card *card, const char *nid) +{ + /* check if user specified own card->id */ + if (card->id[0] != '\0') + return; + mutex_lock(&snd_card_mutex); + snd_card_set_id_no_lock(card, nid, nid); + mutex_unlock(&snd_card_mutex); +} +EXPORT_SYMBOL(snd_card_set_id); + +static ssize_t +card_id_show_attr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = container_of(dev, struct snd_card, card_dev); + return snprintf(buf, PAGE_SIZE, "%s\n", card->id); +} + +static ssize_t +card_id_store_attr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_card *card = container_of(dev, struct snd_card, card_dev); + char buf1[sizeof(card->id)]; + size_t copy = count > sizeof(card->id) - 1 ? + sizeof(card->id) - 1 : count; + size_t idx; + int c; + + for (idx = 0; idx < copy; idx++) { + c = buf[idx]; + if (!isalnum(c) && c != '_' && c != '-') + return -EINVAL; + } + memcpy(buf1, buf, copy); + buf1[copy] = '\0'; + mutex_lock(&snd_card_mutex); + if (!card_id_ok(NULL, buf1)) { + mutex_unlock(&snd_card_mutex); + return -EEXIST; } + strcpy(card->id, buf1); + snd_info_card_id_change(card); + mutex_unlock(&snd_card_mutex); + + return count; +} + +static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); + +static ssize_t +card_number_show_attr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = container_of(dev, struct snd_card, card_dev); + return snprintf(buf, PAGE_SIZE, "%i\n", card->number); } +static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); + +static struct attribute *card_dev_attrs[] = { + &dev_attr_id.attr, + &dev_attr_number.attr, + NULL +}; + +static struct attribute_group card_dev_attr_group = { + .attrs = card_dev_attrs, +}; + +static const struct attribute_group *card_dev_attr_groups[] = { + &card_dev_attr_group, + NULL +}; + /** * snd_card_register - register the soundcard * @card: soundcard structure @@ -532,21 +684,22 @@ static void choose_default_id(struct snd_card *card) * external accesses. Thus, you should call this function at the end * of the initialization of the card. * - * Returns zero otherwise a negative error code if the registrain failed. + * Return: Zero otherwise a negative error code if the registration failed. */ int snd_card_register(struct snd_card *card) { int err; - snd_assert(card != NULL, return -EINVAL); -#ifndef CONFIG_SYSFS_DEPRECATED - if (!card->card_dev) { - card->card_dev = device_create(sound_class, card->dev, 0, - "card%i", card->number); - if (IS_ERR(card->card_dev)) - card->card_dev = NULL; + if (snd_BUG_ON(!card)) + return -EINVAL; + + if (!card->registered) { + err = device_add(&card->card_dev); + if (err < 0) + return err; + card->registered = true; } -#endif + if ((err = snd_device_register_all(card)) < 0) return err; mutex_lock(&snd_card_mutex); @@ -555,12 +708,22 @@ int snd_card_register(struct snd_card *card) mutex_unlock(&snd_card_mutex); return 0; } - if (card->id[0] == '\0') - choose_default_id(card); + if (*card->id) { + /* make a unique id name from the given string */ + char tmpid[sizeof(card->id)]; + memcpy(tmpid, card->id, sizeof(card->id)); + snd_card_set_id_no_lock(card, tmpid, tmpid); + } else { + /* create an id from either shortname or longname */ + const char *src; + src = *card->shortname ? card->shortname : card->longname; + snd_card_set_id_no_lock(card, src, + retrieve_id_from_card_name(src)); + } snd_cards[card->number] = card; mutex_unlock(&snd_card_mutex); init_info_for_card(card); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); #endif @@ -683,7 +846,7 @@ int __exit snd_card_info_done(void) * This function adds the component id string to the supported list. * The component can be referred from the alsa-lib. * - * Returns zero otherwise a negative error code. + * Return: Zero otherwise a negative error code. */ int snd_component_add(struct snd_card *card, const char *component) @@ -717,7 +880,7 @@ EXPORT_SYMBOL(snd_component_add); * This linked-list is used to keep tracking the connection state, * and to avoid the release of busy resources by hotplug. * - * Returns zero or a negative error code. + * Return: zero or a negative error code. */ int snd_card_file_add(struct snd_card *card, struct file *file) { @@ -728,15 +891,15 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENOMEM; mfile->file = file; mfile->disconnected_f_op = NULL; - mfile->next = NULL; + INIT_LIST_HEAD(&mfile->shutdown_list); spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); kfree(mfile); return -ENODEV; } - mfile->next = card->files; - card->files = mfile; + list_add(&mfile->list, &card->files_list); + get_device(&card->card_dev); spin_unlock(&card->files_lock); return 0; } @@ -754,45 +917,32 @@ EXPORT_SYMBOL(snd_card_file_add); * called beforehand, it processes the pending release of * resources. * - * Returns zero or a negative error code. + * Return: Zero or a negative error code. */ int snd_card_file_remove(struct snd_card *card, struct file *file) { - struct snd_monitor_file *mfile, *pfile = NULL; - int last_close = 0; + struct snd_monitor_file *mfile, *found = NULL; spin_lock(&card->files_lock); - mfile = card->files; - while (mfile) { + list_for_each_entry(mfile, &card->files_list, list) { if (mfile->file == file) { - if (pfile) - pfile->next = mfile->next; - else - card->files = mfile->next; + list_del(&mfile->list); + spin_lock(&shutdown_lock); + list_del(&mfile->shutdown_list); + spin_unlock(&shutdown_lock); + if (mfile->disconnected_f_op) + fops_put(mfile->disconnected_f_op); + found = mfile; break; } - pfile = mfile; - mfile = mfile->next; - } - if (mfile && mfile->disconnected_f_op) { - fops_put(mfile->disconnected_f_op); - spin_lock(&shutdown_lock); - list_del(&mfile->shutdown_list); - spin_unlock(&shutdown_lock); } - if (card->files == NULL) - last_close = 1; spin_unlock(&card->files_lock); - if (last_close) { - wake_up(&card->shutdown_sleep); - if (card->free_on_last_close) - snd_card_do_free(card); - } - if (!mfile) { - snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); + if (!found) { + dev_err(card->dev, "card file remove problem (%p)\n", file); return -ENOENT; } - kfree(mfile); + kfree(found); + put_device(&card->card_dev); return 0; } @@ -806,6 +956,8 @@ EXPORT_SYMBOL(snd_card_file_remove); * * Waits until the power-state is changed. * + * Return: Zero if successful, or a negative error code. + * * Note: the power lock must be active before call. */ int snd_power_wait(struct snd_card *card, unsigned int power_state) diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 79f0f16af33..31e8544d7f2 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -26,6 +26,7 @@ #undef HAVE_REALLY_SLOW_DMA_CONTROLLER +#include <linux/export.h> #include <sound/core.h> #include <asm/dma.h> @@ -80,24 +81,32 @@ EXPORT_SYMBOL(snd_dma_disable); * @dma: the dma number * @size: the dma transfer size * - * Returns the current pointer in DMA tranfer buffer in bytes + * Return: The current pointer in DMA transfer buffer in bytes. */ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) { unsigned long flags; - unsigned int result; + unsigned int result, result1; flags = claim_dma_lock(); clear_dma_ff(dma); if (!isa_dma_bridge_buggy) disable_dma(dma); result = get_dma_residue(dma); + /* + * HACK - read the counter again and choose higher value in order to + * avoid reading during counter lower byte roll over if the + * isa_dma_bridge_buggy is set. + */ + result1 = get_dma_residue(dma); if (!isa_dma_bridge_buggy) enable_dma(dma); release_dma_lock(flags); + if (unlikely(result < result1)) + result = result1; #ifdef CONFIG_SND_DEBUG if (result > size) - snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); + pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); #endif if (result >= size || result == 0) return 0; diff --git a/sound/core/jack.c b/sound/core/jack.c new file mode 100644 index 00000000000..8658578eb58 --- /dev/null +++ b/sound/core/jack.c @@ -0,0 +1,260 @@ +/* + * Jack abstraction layer + * + * Copyright 2008 Wolfson Microelectronics + * + * 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/input.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/jack.h> +#include <sound/core.h> + +static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { + SW_HEADPHONE_INSERT, + SW_MICROPHONE_INSERT, + SW_LINEOUT_INSERT, + SW_JACK_PHYSICAL_INSERT, + SW_VIDEOOUT_INSERT, + SW_LINEIN_INSERT, +}; + +static int snd_jack_dev_disconnect(struct snd_device *device) +{ + struct snd_jack *jack = device->device_data; + + if (!jack->input_dev) + return 0; + + /* If the input device is registered with the input subsystem + * then we need to use a different deallocator. */ + if (jack->registered) + input_unregister_device(jack->input_dev); + else + input_free_device(jack->input_dev); + jack->input_dev = NULL; + return 0; +} + +static int snd_jack_dev_free(struct snd_device *device) +{ + struct snd_jack *jack = device->device_data; + + if (jack->private_free) + jack->private_free(jack); + + snd_jack_dev_disconnect(device); + + kfree(jack->id); + kfree(jack); + + return 0; +} + +static int snd_jack_dev_register(struct snd_device *device) +{ + struct snd_jack *jack = device->device_data; + struct snd_card *card = device->card; + int err, i; + + snprintf(jack->name, sizeof(jack->name), "%s %s", + card->shortname, jack->id); + jack->input_dev->name = jack->name; + + /* Default to the sound card device. */ + if (!jack->input_dev->dev.parent) + jack->input_dev->dev.parent = snd_card_get_device_link(card); + + /* Add capabilities for any keys that are enabled */ + for (i = 0; i < ARRAY_SIZE(jack->key); i++) { + int testbit = SND_JACK_BTN_0 >> i; + + if (!(jack->type & testbit)) + continue; + + if (!jack->key[i]) + jack->key[i] = BTN_0 + i; + + input_set_capability(jack->input_dev, EV_KEY, jack->key[i]); + } + + err = input_register_device(jack->input_dev); + if (err == 0) + jack->registered = 1; + + return err; +} + +/** + * snd_jack_new - Create a new jack + * @card: the card instance + * @id: an identifying string for this jack + * @type: a bitmask of enum snd_jack_type values that can be detected by + * this jack + * @jjack: Used to provide the allocated jack object to the caller. + * + * Creates a new jack object. + * + * Return: Zero if successful, or a negative error code on failure. + * On success @jjack will be initialised. + */ +int snd_jack_new(struct snd_card *card, const char *id, int type, + struct snd_jack **jjack) +{ + struct snd_jack *jack; + int err; + int i; + static struct snd_device_ops ops = { + .dev_free = snd_jack_dev_free, + .dev_register = snd_jack_dev_register, + .dev_disconnect = snd_jack_dev_disconnect, + }; + + jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL); + if (jack == NULL) + return -ENOMEM; + + jack->id = kstrdup(id, GFP_KERNEL); + + jack->input_dev = input_allocate_device(); + if (jack->input_dev == NULL) { + err = -ENOMEM; + goto fail_input; + } + + jack->input_dev->phys = "ALSA"; + + jack->type = type; + + for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) + if (type & (1 << i)) + input_set_capability(jack->input_dev, EV_SW, + jack_switch_types[i]); + + err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); + if (err < 0) + goto fail_input; + + *jjack = jack; + + return 0; + +fail_input: + input_free_device(jack->input_dev); + kfree(jack->id); + kfree(jack); + return err; +} +EXPORT_SYMBOL(snd_jack_new); + +/** + * snd_jack_set_parent - Set the parent device for a jack + * + * @jack: The jack to configure + * @parent: The device to set as parent for the jack. + * + * Set the parent for the jack devices in the device tree. This + * function is only valid prior to registration of the jack. If no + * parent is configured then the parent device will be the sound card. + */ +void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) +{ + WARN_ON(jack->registered); + + jack->input_dev->dev.parent = parent; +} +EXPORT_SYMBOL(snd_jack_set_parent); + +/** + * snd_jack_set_key - Set a key mapping on a jack + * + * @jack: The jack to configure + * @type: Jack report type for this key + * @keytype: Input layer key type to be reported + * + * Map a SND_JACK_BTN_ button type to an input layer key, allowing + * reporting of keys on accessories via the jack abstraction. If no + * mapping is provided but keys are enabled in the jack type then + * BTN_n numeric buttons will be reported. + * + * If jacks are not reporting via the input API this call will have no + * effect. + * + * Note that this is intended to be use by simple devices with small + * numbers of keys that can be reported. It is also possible to + * access the input device directly - devices with complex input + * capabilities on accessories should consider doing this rather than + * using this abstraction. + * + * This function may only be called prior to registration of the jack. + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, + int keytype) +{ + int key = fls(SND_JACK_BTN_0) - fls(type); + + WARN_ON(jack->registered); + + if (!keytype || key >= ARRAY_SIZE(jack->key)) + return -EINVAL; + + jack->type |= type; + jack->key[key] = keytype; + + return 0; +} +EXPORT_SYMBOL(snd_jack_set_key); + +/** + * snd_jack_report - Report the current status of a jack + * + * @jack: The jack to report status for + * @status: The current status of the jack + */ +void snd_jack_report(struct snd_jack *jack, int status) +{ + int i; + + if (!jack) + return; + + for (i = 0; i < ARRAY_SIZE(jack->key); i++) { + int testbit = SND_JACK_BTN_0 >> i; + + if (jack->type & testbit) + input_report_key(jack->input_dev, jack->key[i], + status & testbit); + } + + for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) { + int testbit = 1 << i; + if (jack->type & testbit) + input_report_switch(jack->input_dev, + jack_switch_types[i], + status & testbit); + } + + input_sync(jack->input_dev); +} +EXPORT_SYMBOL(snd_jack_report); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("Jack detection support for ALSA"); +MODULE_LICENSE("GPL"); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 920e5780c22..082509eb805 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -21,125 +21,11 @@ * */ -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/init.h> -#include <linux/pci.h> #include <linux/slab.h> #include <linux/mm.h> -#include <linux/seq_file.h> -#include <asm/uaccess.h> #include <linux/dma-mapping.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> +#include <linux/genalloc.h> #include <sound/memalloc.h> -#ifdef CONFIG_SBUS -#include <asm/sbus.h> -#endif - - -MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Memory allocator for ALSA system."); -MODULE_LICENSE("GPL"); - - -/* - */ - -void *snd_malloc_sgbuf_pages(struct device *device, - size_t size, struct snd_dma_buffer *dmab, - size_t *res_size); -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); - -/* - */ - -static DEFINE_MUTEX(list_mutex); -static LIST_HEAD(mem_list_head); - -/* buffer preservation list */ -struct snd_mem_list { - struct snd_dma_buffer buffer; - unsigned int id; - struct list_head list; -}; - -/* id for pre-allocated buffers */ -#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1 - -#ifdef CONFIG_SND_DEBUG -#define __ASTRING__(x) #x -#define snd_assert(expr, args...) do {\ - if (!(expr)) {\ - printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ - args;\ - }\ -} while (0) -#else -#define snd_assert(expr, args...) /**/ -#endif - -/* - * Hacks - */ - -#if defined(__i386__) -/* - * A hack to allocate large buffers via dma_alloc_coherent() - * - * since dma_alloc_coherent always tries GFP_DMA when the requested - * pci memory region is below 32bit, it happens quite often that even - * 2 order of pages cannot be allocated. - * - * so in the following, we allocate at first without dma_mask, so that - * allocation will be done without GFP_DMA. if the area doesn't match - * with the requested region, then realloate with the original dma_mask - * again. - * - * Really, we want to move this type of thing into dma_alloc_coherent() - * so dma_mask doesn't have to be messed with. - */ - -static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, - gfp_t flags) -{ - void *ret; - u64 dma_mask, coherent_dma_mask; - - if (dev == NULL || !dev->dma_mask) - return dma_alloc_coherent(dev, size, dma_handle, flags); - dma_mask = *dev->dma_mask; - coherent_dma_mask = dev->coherent_dma_mask; - *dev->dma_mask = 0xffffffff; /* do without masking */ - dev->coherent_dma_mask = 0xffffffff; /* do without masking */ - ret = dma_alloc_coherent(dev, size, dma_handle, flags); - *dev->dma_mask = dma_mask; /* restore */ - dev->coherent_dma_mask = coherent_dma_mask; /* restore */ - if (ret) { - /* obtained address is out of range? */ - if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) { - /* reallocate with the proper mask */ - dma_free_coherent(dev, size, ret, *dma_handle); - ret = dma_alloc_coherent(dev, size, dma_handle, flags); - } - } else { - /* wish to success now with the proper mask... */ - if (dma_mask != 0xffffffffUL) { - /* allocation with GFP_ATOMIC to avoid the long stall */ - flags &= ~GFP_KERNEL; - flags |= GFP_ATOMIC; - ret = dma_alloc_coherent(dev, size, dma_handle, flags); - } - } - return ret; -} - -/* redefine dma_alloc_coherent for some architectures */ -#undef dma_alloc_coherent -#define dma_alloc_coherent snd_dma_hack_alloc_coherent - -#endif /* arch */ /* * @@ -147,18 +33,6 @@ static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, * */ -static long snd_allocated_pages; /* holding the number of allocated pages */ - -static inline void inc_snd_pages(int order) -{ - snd_allocated_pages += 1 << order; -} - -static inline void dec_snd_pages(int order) -{ - snd_allocated_pages -= 1 << order; -} - /** * snd_malloc_pages - allocate pages with the given size * @size: the size to allocate in bytes @@ -166,20 +40,19 @@ static inline void dec_snd_pages(int order) * * Allocates the physically contiguous pages with the given size. * - * Returns the pointer of the buffer, or NULL if no enoguh memory. + * Return: The pointer of the buffer, or %NULL if no enough memory. */ void *snd_malloc_pages(size_t size, gfp_t gfp_flags) { int pg; - void *res; - snd_assert(size > 0, return NULL); - snd_assert(gfp_flags != 0, return NULL); + if (WARN_ON(!size)) + return NULL; + if (WARN_ON(!gfp_flags)) + return NULL; gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */ pg = get_order(size); - if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) - inc_snd_pages(pg); - return res; + return (void *) __get_free_pages(gfp_flags, pg); } /** @@ -196,7 +69,6 @@ void snd_free_pages(void *ptr, size_t size) if (ptr == NULL) return; pg = get_order(size); - dec_snd_pages(pg); free_pages((unsigned long) ptr, pg); } @@ -211,21 +83,16 @@ void snd_free_pages(void *ptr, size_t size) static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) { int pg; - void *res; gfp_t gfp_flags; - snd_assert(size > 0, return NULL); - snd_assert(dma != NULL, return NULL); + if (WARN_ON(!dma)) + return NULL; pg = get_order(size); gfp_flags = GFP_KERNEL | __GFP_COMP /* compound page lets parts be mapped */ | __GFP_NORETRY /* don't trigger OOM-killer */ | __GFP_NOWARN; /* no stack trace print - this call is non-critical */ - res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); - if (res != NULL) - inc_snd_pages(pg); - - return res; + return dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); } /* free the coherent DMA pages */ @@ -237,43 +104,50 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, if (ptr == NULL) return; pg = get_order(size); - dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } -#endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SBUS - -static void *snd_malloc_sbus_pages(struct device *dev, size_t size, - dma_addr_t *dma_addr) +#ifdef CONFIG_GENERIC_ALLOCATOR +/** + * snd_malloc_dev_iram - allocate memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + * @size: number of bytes to allocate from the iram + * + * This function requires iram phandle provided via of_node + */ +static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) { - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; - void *res; + struct device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - pg = get_order(size); - res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) - inc_snd_pages(pg); - return res; -} + dmab->area = NULL; + dmab->addr = 0; -static void snd_free_sbus_pages(struct device *dev, size_t size, - void *ptr, dma_addr_t dma_addr) -{ - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); - if (ptr == NULL) + if (!pool) return; - pg = get_order(size); - dec_snd_pages(pg); - sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr); } -#endif /* CONFIG_SBUS */ +/** + * snd_free_dev_iram - free allocated specific memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + */ +static void snd_free_dev_iram(struct snd_dma_buffer *dmab) +{ + struct gen_pool *pool = dmab->private_data; + + if (pool && dmab->area) + gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); +} +#endif /* CONFIG_GENERIC_ALLOCATOR */ +#endif /* CONFIG_HAS_DMA */ /* * @@ -291,39 +165,49 @@ static void snd_free_sbus_pages(struct device *dev, size_t size, * * Calls the memory-allocator function for the corresponding * buffer type. - * - * Returns zero if the buffer with the given size is allocated successfuly, - * other a negative value at error. + * + * Return: Zero if the buffer with the given size is allocated successfully, + * otherwise a negative value on error. */ int snd_dma_alloc_pages(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { - snd_assert(size > 0, return -ENXIO); - snd_assert(dmab != NULL, return -ENXIO); + if (WARN_ON(!size)) + return -ENXIO; + if (WARN_ON(!dmab)) + return -ENXIO; dmab->dev.type = type; dmab->dev.dev = device; dmab->bytes = 0; switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, (unsigned long)device); + dmab->area = snd_malloc_pages(size, + (__force gfp_t)(unsigned long)device); dmab->addr = 0; break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_malloc_dev_iram(dmab, size); + if (dmab->area) + break; + /* Internal memory might have limited size and no enough space, + * so if we fail to malloc, try to fetch memory traditionally. + */ + dmab->dev.type = SNDRV_DMA_TYPE_DEV; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; +#endif +#ifdef CONFIG_SND_DMA_SGBUF case SNDRV_DMA_TYPE_DEV_SG: snd_malloc_sgbuf_pages(device, size, dmab, NULL); break; #endif default: - printk(KERN_ERR "snd-malloc: invalid device type %d\n", type); + pr_err("snd-malloc: invalid device type %d\n", type); dmab->area = NULL; dmab->addr = 0; return -ENXIO; @@ -345,24 +229,26 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, * buffer type. When no space is left, this function reduces the size and * tries to allocate again. The size actually allocated is stored in * res_size argument. - * - * Returns zero if the buffer with the given size is allocated successfuly, - * other a negative value at error. + * + * Return: Zero if the buffer with the given size is allocated successfully, + * otherwise a negative value on error. */ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { int err; - snd_assert(size > 0, return -ENXIO); - snd_assert(dmab != NULL, return -ENXIO); - while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) { + size_t aligned_size; if (err != -ENOMEM) return err; - size >>= 1; if (size <= PAGE_SIZE) return -ENOMEM; + aligned_size = PAGE_SIZE << get_order(size); + if (size != aligned_size) + size = aligned_size; + else + size >>= 1; } if (! dmab->area) return -ENOMEM; @@ -382,272 +268,25 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_free_dev_iram(dmab); + break; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; +#endif +#ifdef CONFIG_SND_DMA_SGBUF case SNDRV_DMA_TYPE_DEV_SG: snd_free_sgbuf_pages(dmab); break; #endif default: - printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type); - } -} - - -/** - * snd_dma_get_reserved - get the reserved buffer for the given device - * @dmab: the buffer allocation record to store - * @id: the buffer id - * - * Looks for the reserved-buffer list and re-uses if the same buffer - * is found in the list. When the buffer is found, it's removed from the free list. - * - * Returns the size of buffer if the buffer is found, or zero if not found. - */ -size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) -{ - struct snd_mem_list *mem; - - snd_assert(dmab, return 0); - - mutex_lock(&list_mutex); - list_for_each_entry(mem, &mem_list_head, list) { - if (mem->id == id && - (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL || - ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) { - struct device *dev = dmab->dev.dev; - list_del(&mem->list); - *dmab = mem->buffer; - if (dmab->dev.dev == NULL) - dmab->dev.dev = dev; - kfree(mem); - mutex_unlock(&list_mutex); - return dmab->bytes; - } - } - mutex_unlock(&list_mutex); - return 0; -} - -/** - * snd_dma_reserve_buf - reserve the buffer - * @dmab: the buffer to reserve - * @id: the buffer id - * - * Reserves the given buffer as a reserved buffer. - * - * Returns zero if successful, or a negative code at error. - */ -int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id) -{ - struct snd_mem_list *mem; - - snd_assert(dmab, return -EINVAL); - mem = kmalloc(sizeof(*mem), GFP_KERNEL); - if (! mem) - return -ENOMEM; - mutex_lock(&list_mutex); - mem->buffer = *dmab; - mem->id = id; - list_add_tail(&mem->list, &mem_list_head); - mutex_unlock(&list_mutex); - return 0; -} - -/* - * purge all reserved buffers - */ -static void free_all_reserved_pages(void) -{ - struct list_head *p; - struct snd_mem_list *mem; - - mutex_lock(&list_mutex); - while (! list_empty(&mem_list_head)) { - p = mem_list_head.next; - mem = list_entry(p, struct snd_mem_list, list); - list_del(p); - snd_dma_free_pages(&mem->buffer); - kfree(mem); - } - mutex_unlock(&list_mutex); -} - - -#ifdef CONFIG_PROC_FS -/* - * proc file interface - */ -#define SND_MEM_PROC_FILE "driver/snd-page-alloc" -static struct proc_dir_entry *snd_mem_proc; - -static int snd_mem_proc_read(struct seq_file *seq, void *offset) -{ - long pages = snd_allocated_pages >> (PAGE_SHIFT-12); - struct snd_mem_list *mem; - int devno; - static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; - - mutex_lock(&list_mutex); - seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n", - pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); - devno = 0; - list_for_each_entry(mem, &mem_list_head, list) { - devno++; - seq_printf(seq, "buffer %d : ID %08x : type %s\n", - devno, mem->id, types[mem->buffer.dev.type]); - seq_printf(seq, " addr = 0x%lx, size = %d bytes\n", - (unsigned long)mem->buffer.addr, - (int)mem->buffer.bytes); + pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type); } - mutex_unlock(&list_mutex); - return 0; -} - -static int snd_mem_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, snd_mem_proc_read, NULL); -} - -/* FIXME: for pci only - other bus? */ -#ifdef CONFIG_PCI -#define gettoken(bufp) strsep(bufp, " \t\n") - -static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer, - size_t count, loff_t * ppos) -{ - char buf[128]; - char *token, *p; - - if (count > sizeof(buf) - 1) - return -EINVAL; - if (copy_from_user(buf, buffer, count)) - return -EFAULT; - buf[count] = '\0'; - - p = buf; - token = gettoken(&p); - if (! token || *token == '#') - return count; - if (strcmp(token, "add") == 0) { - char *endp; - int vendor, device, size, buffers; - long mask; - int i, alloced; - struct pci_dev *pci; - - if ((token = gettoken(&p)) == NULL || - (vendor = simple_strtol(token, NULL, 0)) <= 0 || - (token = gettoken(&p)) == NULL || - (device = simple_strtol(token, NULL, 0)) <= 0 || - (token = gettoken(&p)) == NULL || - (mask = simple_strtol(token, NULL, 0)) < 0 || - (token = gettoken(&p)) == NULL || - (size = memparse(token, &endp)) < 64*1024 || - size > 16*1024*1024 /* too big */ || - (token = gettoken(&p)) == NULL || - (buffers = simple_strtol(token, NULL, 0)) <= 0 || - buffers > 4) { - printk(KERN_ERR "snd-page-alloc: invalid proc write format\n"); - return count; - } - vendor &= 0xffff; - device &= 0xffff; - - alloced = 0; - pci = NULL; - while ((pci = pci_get_device(vendor, device, pci)) != NULL) { - if (mask > 0 && mask < 0xffffffff) { - if (pci_set_dma_mask(pci, mask) < 0 || - pci_set_consistent_dma_mask(pci, mask) < 0) { - printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device); - pci_dev_put(pci); - return count; - } - } - for (i = 0; i < buffers; i++) { - struct snd_dma_buffer dmab; - memset(&dmab, 0, sizeof(dmab)); - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), - size, &dmab) < 0) { - printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size); - pci_dev_put(pci); - return count; - } - snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci)); - } - alloced++; - } - if (! alloced) { - for (i = 0; i < buffers; i++) { - struct snd_dma_buffer dmab; - memset(&dmab, 0, sizeof(dmab)); - /* FIXME: We can allocate only in ZONE_DMA - * without a device pointer! - */ - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL, - size, &dmab) < 0) { - printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size); - break; - } - snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device)); - } - } - } else if (strcmp(token, "erase") == 0) - /* FIXME: need for releasing each buffer chunk? */ - free_all_reserved_pages(); - else - printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n"); - return count; } -#endif /* CONFIG_PCI */ - -static const struct file_operations snd_mem_proc_fops = { - .owner = THIS_MODULE, - .open = snd_mem_proc_open, - .read = seq_read, -#ifdef CONFIG_PCI - .write = snd_mem_proc_write, -#endif - .llseek = seq_lseek, - .release = single_release, -}; - -#endif /* CONFIG_PROC_FS */ - -/* - * module entry - */ - -static int __init snd_mem_init(void) -{ -#ifdef CONFIG_PROC_FS - snd_mem_proc = create_proc_entry(SND_MEM_PROC_FILE, 0644, NULL); - if (snd_mem_proc) - snd_mem_proc->proc_fops = &snd_mem_proc_fops; -#endif - return 0; -} - -static void __exit snd_mem_exit(void) -{ - remove_proc_entry(SND_MEM_PROC_FILE, NULL); - free_all_reserved_pages(); - if (snd_allocated_pages > 0) - printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages); -} - - -module_init(snd_mem_init) -module_exit(snd_mem_exit) - /* * exports @@ -656,8 +295,5 @@ EXPORT_SYMBOL(snd_dma_alloc_pages); EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); -EXPORT_SYMBOL(snd_dma_get_reserved_buf); -EXPORT_SYMBOL(snd_dma_reserve_buf); - EXPORT_SYMBOL(snd_malloc_pages); EXPORT_SYMBOL(snd_free_pages); diff --git a/sound/core/memory.c b/sound/core/memory.c index 1161158582a..36c0f1a2e18 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -20,6 +20,7 @@ * */ +#include <linux/export.h> #include <asm/io.h> #include <asm/uaccess.h> #include <sound/core.h> @@ -32,7 +33,7 @@ * * Copies the data from mmio-space to user-space. * - * Returns zero if successful, or non-zero on failure. + * Return: Zero if successful, or non-zero on failure. */ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) { @@ -65,7 +66,7 @@ EXPORT_SYMBOL(copy_to_user_fromio); * * Copies the data from user-space to mmio-space. * - * Returns zero if successful, or non-zero on failure. + * Return: Zero if successful, or non-zero on failure. */ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) { diff --git a/sound/core/misc.c b/sound/core/misc.c index 102d1c36cf2..30e027ecf4d 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -20,10 +20,27 @@ */ #include <linux/init.h> +#include <linux/export.h> +#include <linux/moduleparam.h> #include <linux/time.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <sound/core.h> +#ifdef CONFIG_SND_DEBUG + +#ifdef CONFIG_SND_DEBUG_VERBOSE +#define DEFAULT_DEBUG_LEVEL 2 +#else +#define DEFAULT_DEBUG_LEVEL 1 +#endif + +static int debug = DEFAULT_DEBUG_LEVEL; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0 = disable)"); + +#endif /* CONFIG_SND_DEBUG */ + void release_and_free_resource(struct resource *res) { if (res) { @@ -35,53 +52,60 @@ void release_and_free_resource(struct resource *res) EXPORT_SYMBOL(release_and_free_resource); #ifdef CONFIG_SND_VERBOSE_PRINTK -void snd_verbose_printk(const char *file, int line, const char *format, ...) +/* strip the leading path if the given path is absolute */ +static const char *sanity_file_name(const char *path) { - va_list args; - - if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { - char tmp[] = "<0>"; - tmp[1] = format[1]; - printk("%sALSA %s:%d: ", tmp, file, line); - format += 3; - } else { - printk("ALSA %s:%d: ", file, line); - } - va_start(args, format); - vprintk(format, args); - va_end(args); + if (*path == '/') + return strrchr(path, '/') + 1; + else + return path; } - -EXPORT_SYMBOL(snd_verbose_printk); #endif -#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) -void snd_verbose_printd(const char *file, int line, const char *format, ...) +#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK) +void __snd_printk(unsigned int level, const char *path, int line, + const char *format, ...) { va_list args; - - if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { - char tmp[] = "<0>"; - tmp[1] = format[1]; - printk("%sALSA %s:%d: ", tmp, file, line); - format += 3; - } else { - printk(KERN_DEBUG "ALSA %s:%d: ", file, line); - } +#ifdef CONFIG_SND_VERBOSE_PRINTK + int kern_level; + struct va_format vaf; + char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; +#endif + +#ifdef CONFIG_SND_DEBUG + if (debug < level) + return; +#endif + va_start(args, format); +#ifdef CONFIG_SND_VERBOSE_PRINTK + vaf.fmt = format; + vaf.va = &args; + + kern_level = printk_get_level(format); + if (kern_level) { + const char *end_of_header = printk_skip_level(format); + memcpy(verbose_fmt, format, end_of_header - format); + vaf.fmt = end_of_header; + } else if (level) + memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1); + printk(verbose_fmt, sanity_file_name(path), line, &vaf); + +#else vprintk(format, args); +#endif va_end(args); - } - -EXPORT_SYMBOL(snd_verbose_printd); +EXPORT_SYMBOL_GPL(__snd_printk); #endif #ifdef CONFIG_PCI #include <linux/pci.h> /** - * snd_pci_quirk_lookup - look up a PCI SSID quirk list - * @pci: pci_dev handle + * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list + * @vendor: PCI SSV id + * @device: PCI SSD id * @list: quirk list, terminated by a null entry * * Look through the given quirk list and finds a matching entry @@ -91,16 +115,39 @@ EXPORT_SYMBOL(snd_verbose_printd); * Returns the matched entry pointer, or NULL if nothing matched. */ const struct snd_pci_quirk * -snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) +snd_pci_quirk_lookup_id(u16 vendor, u16 device, + const struct snd_pci_quirk *list) { const struct snd_pci_quirk *q; - for (q = list; q->subvendor; q++) - if (q->subvendor == pci->subsystem_vendor && - (!q->subdevice || q->subdevice == pci->subsystem_device)) + for (q = list; q->subvendor; q++) { + if (q->subvendor != vendor) + continue; + if (!q->subdevice || + (device & q->subdevice_mask) == q->subdevice) return q; + } return NULL; } +EXPORT_SYMBOL(snd_pci_quirk_lookup_id); +/** + * snd_pci_quirk_lookup - look up a PCI SSID quirk list + * @pci: pci_dev handle + * @list: quirk list, terminated by a null entry + * + * Look through the given quirk list and finds a matching entry + * with the same PCI SSID. When subdevice is 0, all subdevice + * values may match. + * + * Returns the matched entry pointer, or NULL if nothing matched. + */ +const struct snd_pci_quirk * +snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) +{ + return snd_pci_quirk_lookup_id(pci->subsystem_vendor, + pci->subsystem_device, + list); +} EXPORT_SYMBOL(snd_pci_quirk_lookup); #endif diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c index 9ded30d0e97..05b58d4fc2b 100644 --- a/sound/core/oss/copy.c +++ b/sound/core/oss/copy.c @@ -32,17 +32,18 @@ static snd_pcm_sframes_t copy_transfer(struct snd_pcm_plugin *plugin, unsigned int channel; unsigned int nchannels; - snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) + return -ENXIO; if (frames == 0) return 0; nchannels = plugin->src_format.channels; for (channel = 0; channel < nchannels; channel++) { - snd_assert(src_channels->area.first % 8 == 0 && - src_channels->area.step % 8 == 0, - return -ENXIO); - snd_assert(dst_channels->area.first % 8 == 0 && - dst_channels->area.step % 8 == 0, - return -ENXIO); + if (snd_BUG_ON(src_channels->area.first % 8 || + src_channels->area.step % 8)) + return -ENXIO; + if (snd_BUG_ON(dst_channels->area.first % 8 || + dst_channels->area.step % 8)) + return -ENXIO; if (!src_channels->enabled) { if (dst_channels->wanted) snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); @@ -66,15 +67,20 @@ int snd_pcm_plugin_build_copy(struct snd_pcm_substream *plug, struct snd_pcm_plugin *plugin; int width; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(src_format->format == dst_format->format, return -ENXIO); - snd_assert(src_format->rate == dst_format->rate, return -ENXIO); - snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + if (snd_BUG_ON(src_format->format != dst_format->format)) + return -ENXIO; + if (snd_BUG_ON(src_format->rate != dst_format->rate)) + return -ENXIO; + if (snd_BUG_ON(src_format->channels != dst_format->channels)) + return -ENXIO; width = snd_pcm_format_physical_width(src_format->format); - snd_assert(width > 0, return -ENXIO); + if (snd_BUG_ON(width <= 0)) + return -ENXIO; err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, 0, &plugin); diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c index f874f6ca365..6faa1d71920 100644 --- a/sound/core/oss/io.c +++ b/sound/core/oss/io.c @@ -39,14 +39,17 @@ static snd_pcm_sframes_t io_playback_transfer(struct snd_pcm_plugin *plugin, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { - snd_assert(plugin != NULL, return -ENXIO); - snd_assert(src_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin)) + return -ENXIO; + if (snd_BUG_ON(!src_channels)) + return -ENXIO; if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { return pcm_write(plugin->plug, src_channels->area.addr, frames); } else { int channel, channels = plugin->dst_format.channels; void **bufs = (void**)plugin->extra_data; - snd_assert(bufs != NULL, return -ENXIO); + if (snd_BUG_ON(!bufs)) + return -ENXIO; for (channel = 0; channel < channels; channel++) { if (src_channels[channel].enabled) bufs[channel] = src_channels[channel].area.addr; @@ -62,14 +65,17 @@ static snd_pcm_sframes_t io_capture_transfer(struct snd_pcm_plugin *plugin, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { - snd_assert(plugin != NULL, return -ENXIO); - snd_assert(dst_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin)) + return -ENXIO; + if (snd_BUG_ON(!dst_channels)) + return -ENXIO; if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { return pcm_read(plugin->plug, dst_channels->area.addr, frames); } else { int channel, channels = plugin->dst_format.channels; void **bufs = (void**)plugin->extra_data; - snd_assert(bufs != NULL, return -ENXIO); + if (snd_BUG_ON(!bufs)) + return -ENXIO; for (channel = 0; channel < channels; channel++) { if (dst_channels[channel].enabled) bufs[channel] = dst_channels[channel].area.addr; @@ -107,9 +113,11 @@ int snd_pcm_plugin_build_io(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format format; struct snd_pcm_plugin *plugin; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(plug != NULL && params != NULL, return -ENXIO); + if (snd_BUG_ON(!plug || !params)) + return -ENXIO; format.format = params_format(params); format.rate = params_rate(params); format.channels = params_channels(params); diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index da3dbd41669..2045697f449 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -90,22 +90,20 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { - struct linear_priv *data; - - snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); - data = (struct linear_priv *)plugin->extra_data; + if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) + return -ENXIO; if (frames == 0) return 0; #ifdef CONFIG_SND_DEBUG { unsigned int channel; for (channel = 0; channel < plugin->src_format.channels; channel++) { - snd_assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0, - return -ENXIO); - snd_assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0, - return -ENXIO); + if (snd_BUG_ON(src_channels[channel].area.first % 8 || + src_channels[channel].area.step % 8)) + return -ENXIO; + if (snd_BUG_ON(dst_channels[channel].area.first % 8 || + dst_channels[channel].area.step % 8)) + return -ENXIO; } } #endif @@ -113,7 +111,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct linear_priv *data, int src_format, int dst_format) +static void init_data(struct linear_priv *data, + snd_pcm_format_t src_format, snd_pcm_format_t dst_format) { int src_le, dst_le, src_bytes, dst_bytes; @@ -139,9 +138,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format) if (snd_pcm_format_signed(src_format) != snd_pcm_format_signed(dst_format)) { if (dst_le) - data->flip = cpu_to_le32(0x80000000); + data->flip = (__force u32)cpu_to_le32(0x80000000); else - data->flip = cpu_to_be32(0x80000000); + data->flip = (__force u32)cpu_to_be32(0x80000000); } } @@ -154,13 +153,17 @@ int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug, struct linear_priv *data; struct snd_pcm_plugin *plugin; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(src_format->rate == dst_format->rate, return -ENXIO); - snd_assert(src_format->channels == dst_format->channels, return -ENXIO); - snd_assert(snd_pcm_format_linear(src_format->format) && - snd_pcm_format_linear(dst_format->format), return -ENXIO); + if (snd_BUG_ON(src_format->rate != dst_format->rate)) + return -ENXIO; + if (snd_BUG_ON(src_format->channels != dst_format->channels)) + return -ENXIO; + if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) || + !snd_pcm_format_linear(dst_format->format))) + return -ENXIO; err = snd_pcm_plugin_build(plug, "linear format conversion", src_format, dst_format, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 75daed298a1..5e6349f00ec 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/string.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/control.h> @@ -43,18 +44,27 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) struct snd_mixer_oss_file *fmixer; int err; + err = nonseekable_open(inode, file); + if (err < 0) + return err; + card = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_MIXER); if (card == NULL) return -ENODEV; - if (card->mixer_oss == NULL) + if (card->mixer_oss == NULL) { + snd_card_unref(card); return -ENODEV; + } err = snd_card_file_add(card, file); - if (err < 0) + if (err < 0) { + snd_card_unref(card); return err; + } fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL); if (fmixer == NULL) { snd_card_file_remove(card, file); + snd_card_unref(card); return -ENOMEM; } fmixer->card = card; @@ -63,8 +73,10 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) if (!try_module_get(card->module)) { kfree(fmixer); snd_card_file_remove(card, file); + snd_card_unref(card); return -EFAULT; } + snd_card_unref(card); return 0; } @@ -73,7 +85,7 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file) struct snd_mixer_oss_file *fmixer; if (file->private_data) { - fmixer = (struct snd_mixer_oss_file *) file->private_data; + fmixer = file->private_data; module_put(fmixer->card->module); snd_card_file_remove(fmixer->card, file); kfree(fmixer); @@ -186,9 +198,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ int err; - if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + unsigned int index; + if ((err = mixer->get_recsrc(fmixer, &index)) < 0) return err; - result = 1 << result; + result = 1 << index; } else { struct snd_mixer_oss_slot *pslot; int chn; @@ -210,6 +223,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr struct snd_mixer_oss *mixer = fmixer->mixer; struct snd_mixer_oss_slot *pslot; int chn, active; + unsigned int index; int result = 0; if (mixer == NULL) @@ -218,8 +232,8 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; mixer->put_recsrc(fmixer, ffz(~recsrc)); - mixer->get_recsrc(fmixer, &result); - result = 1 << result; + mixer->get_recsrc(fmixer, &index); + result = 1 << index; } for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; @@ -257,8 +271,10 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot) result = pslot->get_volume(fmixer, pslot, &left, &right); if (!pslot->stereo) right = left; - snd_assert(left >= 0 && left <= 100, return -EIO); - snd_assert(right >= 0 && right <= 100, return -EIO); + if (snd_BUG_ON(left < 0 || left > 100)) + return -EIO; + if (snd_BUG_ON(right < 0 || right > 100)) + return -EIO; if (result >= 0) { pslot->volume[0] = left; pslot->volume[1] = right; @@ -298,7 +314,8 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int int __user *p = argp; int tmp; - snd_assert(fmixer != NULL, return -ENXIO); + if (snd_BUG_ON(!fmixer)) + return -ENXIO; if (((cmd >> 8) & 0xff) == 'M') { switch (cmd) { case SOUND_MIXER_INFO: @@ -361,14 +378,15 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg); + return snd_mixer_oss_ioctl1(file->private_data, cmd, arg); } int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg) { struct snd_mixer_oss_file fmixer; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; if (card->mixer_oss == NULL) return -ENXIO; memset(&fmixer, 0, sizeof(fmixer)); @@ -393,6 +411,7 @@ static const struct file_operations snd_mixer_oss_f_ops = .owner = THIS_MODULE, .open = snd_mixer_oss_open, .release = snd_mixer_oss_release, + .llseek = no_llseek, .unlocked_ioctl = snd_mixer_oss_ioctl, .compat_ioctl = snd_mixer_oss_ioctl_compat, }; @@ -488,7 +507,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, name); + strlcpy(id.name, name, sizeof(id.name)); id.index = index; return snd_ctl_find_id(card, &id); } @@ -573,7 +592,7 @@ static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *left, int *right) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; *left = *right = 100; if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { @@ -609,8 +628,10 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) + if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { + up_read(&card->controls_rwsem); return; + } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) @@ -649,7 +670,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, return; down_read(&card->controls_rwsem); if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { - up_read(&fmixer->card->controls_rwsem); + up_read(&card->controls_rwsem); return; } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -682,12 +703,15 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int left, int right) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, + slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { @@ -696,19 +720,27 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer, if (left || right) { if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0); if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1); if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); } else { if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0); } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1); } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); } @@ -720,7 +752,7 @@ static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; int left, right; left = right = 1; @@ -733,7 +765,7 @@ static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; int left, right; left = right = 1; @@ -746,7 +778,7 @@ static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); return 0; @@ -756,7 +788,7 @@ static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); return 0; @@ -777,7 +809,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -793,7 +825,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (!(mixer->mask_recsrc & (1 << idx))) continue; pslot = &mixer->slots[idx]; - slot = (struct slot *)pslot->private_data; + slot = pslot->private_data; if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) continue; if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) @@ -806,6 +838,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -827,7 +860,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -841,7 +874,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (!(mixer->mask_recsrc & (1 << idx))) continue; pslot = &mixer->slots[idx]; - slot = (struct slot *)pslot->private_data; + slot = pslot->private_data; if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) continue; if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) @@ -860,6 +893,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -905,7 +939,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn) { - struct slot *p = (struct slot *)chn->private_data; + struct slot *p = chn->private_data; if (p) { if (p->allocated && p->assigned) { kfree(p->assigned->name); @@ -1019,6 +1053,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); @@ -1034,6 +1069,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)) { @@ -1139,7 +1175,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_mixer_oss *mixer = entry->private_data; - char line[128], str[32], idxstr[16], *cptr; + char line[128], str[32], idxstr[16]; + const char *cptr; int ch, idx; struct snd_mixer_oss_assign_table *tbl; struct slot *slot; @@ -1150,7 +1187,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0) break; if (ch >= SNDRV_OSS_MAX_MIXERS) { - snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str); + pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n", + str); continue; } cptr = snd_info_get_str(str, cptr, sizeof(str)); @@ -1164,7 +1202,7 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, snd_info_get_str(idxstr, cptr, sizeof(idxstr)); idx = simple_strtoul(idxstr, NULL, 10); if (idx >= 0x4000) { /* too big */ - snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx); + pr_err("ALSA: mixer_oss: invalid index %d\n", idx); continue; } mutex_lock(&mixer->reg_mutex); @@ -1175,7 +1213,7 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, goto __unlock; tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); if (! tbl) { - snd_printk(KERN_ERR "mixer_oss: no memory\n"); + pr_err("ALSA: mixer_oss: no memory\n"); goto __unlock; } tbl->oss_id = ch; @@ -1235,7 +1273,9 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */ { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */ { SOUND_MIXER_PCM, "PCM", 0 }, - { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, + { SOUND_MIXER_SPEAKER, "Beep", 0 }, + { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, /* fallback */ + { SOUND_MIXER_SPEAKER, "Speaker", 0 }, /* fallback */ { SOUND_MIXER_LINE, "Line", 0 }, { SOUND_MIXER_MIC, "Mic", 0 }, { SOUND_MIXER_CD, "CD", 0 }, @@ -1257,6 +1297,8 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) { SOUND_MIXER_DIGITAL3, "Digital", 2 }, { SOUND_MIXER_PHONEIN, "Phone", 0 }, { SOUND_MIXER_PHONEOUT, "Master Mono", 0 }, + { SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/ + { SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/ { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */ { SOUND_MIXER_VIDEO, "Video", 0 }, { SOUND_MIXER_RADIO, "Radio", 0 }, @@ -1282,9 +1324,11 @@ static int snd_mixer_oss_free1(void *private) struct snd_card *card; int idx; - snd_assert(mixer != NULL, return -ENXIO); + if (!mixer) + return 0; card = mixer->card; - snd_assert(mixer == card->mixer_oss, return -ENXIO); + if (snd_BUG_ON(mixer != card->mixer_oss)) + return -ENXIO; card->mixer_oss = NULL; for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) { struct snd_mixer_oss_slot *chn = &mixer->slots[idx]; @@ -1300,20 +1344,18 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd) struct snd_mixer_oss *mixer; if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) { - char name[128]; int idx, err; mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL); if (mixer == NULL) return -ENOMEM; mutex_init(&mixer->reg_mutex); - sprintf(name, "mixer%i%i", card->number, 0); if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, card, 0, - &snd_mixer_oss_f_ops, card, - name)) < 0) { - snd_printk(KERN_ERR "unable to register OSS mixer device %i:%i\n", - card->number, 0); + &snd_mixer_oss_f_ops, card)) < 0) { + dev_err(card->dev, + "unable to register OSS mixer device %i:%i\n", + card->number, 0); kfree(mixer); return err; } @@ -1322,7 +1364,8 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd) if (*card->mixername) strlcpy(mixer->name, card->mixername, sizeof(mixer->name)); else - strlcpy(mixer->name, name, sizeof(mixer->name)); + snprintf(mixer->name, sizeof(mixer->name), + "mixer%i", card->number); #ifdef SNDRV_OSS_INFO_DEV_MIXERS snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, card->number, diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index 77f96194a0e..7915564bd39 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -252,19 +252,20 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, { struct mulaw_priv *data; - snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) + return -ENXIO; if (frames == 0) return 0; #ifdef CONFIG_SND_DEBUG { unsigned int channel; for (channel = 0; channel < plugin->src_format.channels; channel++) { - snd_assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0, - return -ENXIO); - snd_assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0, - return -ENXIO); + if (snd_BUG_ON(src_channels[channel].area.first % 8 || + src_channels[channel].area.step % 8)) + return -ENXIO; + if (snd_BUG_ON(dst_channels[channel].area.first % 8 || + dst_channels[channel].area.step % 8)) + return -ENXIO; } } #endif @@ -273,7 +274,7 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct mulaw_priv *data, int format) +static void init_data(struct mulaw_priv *data, snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN data->cvt_endian = snd_pcm_format_big_endian(format) > 0; @@ -305,11 +306,14 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format *format; mulaw_f func; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(src_format->rate == dst_format->rate, return -ENXIO); - snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + if (snd_BUG_ON(src_format->rate != dst_format->rate)) + return -ENXIO; + if (snd_BUG_ON(src_format->channels != dst_format->channels)) + return -ENXIO; if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { format = src_format; @@ -323,7 +327,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug, snd_BUG(); return -EINVAL; } - snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); + if (snd_BUG_ON(!snd_pcm_format_linear(format->format))) + return -ENXIO; err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", src_format, dst_format, diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 4c601b192dd..ada69d7a8d7 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -30,7 +30,8 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/vmalloc.h> -#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/math64.h> #include <linux/string.h> #include <sound/core.h> #include <sound/minors.h> @@ -40,12 +41,13 @@ #include <sound/info.h> #include <linux/soundcard.h> #include <sound/initval.h> +#include <sound/mixer_oss.h> #define OSS_ALSAEMULVER _SIOR ('M', 249, int) static int dsp_map[SNDRV_CARDS]; static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; -static int nonblock_open = 1; +static bool nonblock_open = 1; MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); MODULE_DESCRIPTION("PCM OSS emulation for ALSA."); @@ -59,7 +61,6 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); -extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg); static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); @@ -452,7 +453,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, } else { *params = *save; max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); - snd_assert(max >= 0, return -EINVAL); + if (max < 0) { + kfree(save); + return max; + } last = 1; } _end: @@ -461,7 +465,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, v = snd_pcm_hw_param_last(pcm, params, var, dir); else v = snd_pcm_hw_param_first(pcm, params, var, dir); - snd_assert(v >= 0, return -EINVAL); + snd_BUG_ON(v < 0); return v; } @@ -616,9 +620,7 @@ static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames) #else { u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes; - u32 rem; - div64_32(&bsize, buffer_size, &rem); - return (long)bsize; + return div_u64(bsize, buffer_size); } #endif } @@ -632,6 +634,12 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); } +static inline +snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) +{ + return runtime->hw_ptr_interrupt; +} + /* define extended formats in the recent OSS versions (if any) */ /* linear formats */ #define AFMT_S32_LE 0x00001000 @@ -648,7 +656,7 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) #define AFMT_AC3 0x00000400 #define AFMT_VORBIS 0x00000800 -static int snd_pcm_oss_format_from(int format) +static snd_pcm_format_t snd_pcm_oss_format_from(int format) { switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; @@ -672,7 +680,7 @@ static int snd_pcm_oss_format_from(int format) } } -static int snd_pcm_oss_format_to(int format) +static int snd_pcm_oss_format_to(snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; @@ -778,7 +786,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, while (oss_period_size * oss_periods > oss_buffer_size) oss_period_size /= 2; - snd_assert(oss_period_size >= 16, return -EINVAL); + if (oss_period_size < 16) + return -EINVAL; runtime->oss.period_bytes = oss_period_size; runtime->oss.period_frames = 1; runtime->oss.periods = oss_periods; @@ -834,7 +843,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) size_t oss_frame_size; int err; int direct; - int format, sformat, n; + snd_pcm_format_t format, sformat; + int n; struct snd_mask sformat_mask; struct snd_mask mask; @@ -844,7 +854,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) params = kmalloc(sizeof(*params), GFP_KERNEL); sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); if (!sw_params || !params || !sparams) { - snd_printd("No memory\n"); + pcm_dbg(substream->pcm, "No memory\n"); err = -ENOMEM; goto failure; } @@ -859,15 +869,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { - snd_printd("No usable accesses\n"); + pcm_dbg(substream->pcm, "No usable accesses\n"); err = -EINVAL; goto failure; } @@ -882,29 +892,33 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, &sformat_mask); - if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { - for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { - if (snd_mask_test(&sformat_mask, sformat) && + if ((__force int)sformat < 0 || + !snd_mask_test(&sformat_mask, (__force int)sformat)) { + for (sformat = (__force snd_pcm_format_t)0; + (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; + sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { + if (snd_mask_test(&sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } - if (sformat > SNDRV_PCM_FORMAT_LAST) { - snd_printd("Cannot find a format!!!\n"); + if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) { + pcm_dbg(substream->pcm, "Cannot find a format!!!\n"); err = -EINVAL; goto failure; } } - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); - snd_assert(err >= 0, goto failure); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); + if (err < 0) + goto failure; if (direct) { memcpy(params, sparams, sizeof(*params)); } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd_pcm_oss_format_from(runtime->oss.format), 0); + (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -928,14 +942,16 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) if ((err = snd_pcm_plug_format_plugins(substream, params, sparams)) < 0) { - snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); + pcm_dbg(substream->pcm, + "snd_pcm_plug_format_plugins failed: %i\n", err); snd_pcm_oss_plugin_clear(substream); goto failure; } if (runtime->oss.plugin_first) { struct snd_pcm_plugin *plugin; if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) { - snd_printd("snd_pcm_plugin_build_io failed: %i\n", err); + pcm_dbg(substream->pcm, + "snd_pcm_plugin_build_io failed: %i\n", err); snd_pcm_oss_plugin_clear(substream); goto failure; } @@ -958,16 +974,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL); - snd_assert(err >= 0, goto failure); + if (err < 0) + goto failure; err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS, runtime->oss.periods, NULL); - snd_assert(err >= 0, goto failure); + if (err < 0) + goto failure; snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) { - snd_printd("HW_PARAMS failed: %i\n", err); + pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err); goto failure; } @@ -1000,13 +1018,16 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) } if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) { - snd_printd("SW_PARAMS failed: %i\n", err); + pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err); goto failure; } runtime->oss.periods = params_periods(sparams); oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams)); - snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure); + if (oss_period_size < 0) { + err = -EINVAL; + goto failure; + } #ifdef CONFIG_SND_PCM_OSS_PLUGINS if (runtime->oss.plugin_first) { err = snd_pcm_plug_alloc(substream, oss_period_size); @@ -1017,7 +1038,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) oss_period_size *= oss_frame_size; oss_buffer_size = oss_period_size * runtime->oss.periods; - snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure); + if (oss_buffer_size < 0) { + err = -EINVAL; + goto failure; + } runtime->oss.period_bytes = oss_period_size; runtime->oss.buffer_bytes = oss_buffer_size; @@ -1033,10 +1057,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) runtime->oss.channels = params_channels(params); runtime->oss.rate = params_rate(params); - runtime->oss.params = 0; - runtime->oss.prepare = 1; vfree(runtime->oss.buffer); runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); + if (!runtime->oss.buffer) { + err = -ENOMEM; + goto failure; + } + + runtime->oss.params = 0; + runtime->oss.prepare = 1; runtime->oss.buffer_used = 0; if (runtime->dma_area) snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); @@ -1069,7 +1098,8 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil return err; } } - snd_assert(asubstream != NULL, return -EIO); + if (!asubstream) + return -EIO; if (r_substream) *r_substream = asubstream; return 0; @@ -1082,11 +1112,12 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); if (err < 0) { - snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); + pcm_dbg(substream->pcm, + "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); return err; } runtime->oss.prepare = 0; - runtime->oss.prev_hw_ptr_interrupt = 0; + runtime->oss.prev_hw_ptr_period = 0; runtime->oss.period_ptr = 0; runtime->oss.buffer_used = 0; @@ -1147,10 +1178,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (runtime->status->state == SNDRV_PCM_STATE_XRUN || runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG - if (runtime->status->state == SNDRV_PCM_STATE_XRUN) - printk("pcm_oss: write: recovering from XRUN\n"); - else - printk("pcm_oss: write: recovering from SUSPEND\n"); + pcm_dbg(substream->pcm, + "pcm_oss: write: recovering from %s\n", + runtime->status->state == SNDRV_PCM_STATE_XRUN ? + "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); if (ret < 0) @@ -1159,10 +1190,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); } if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1183,10 +1214,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p if (runtime->status->state == SNDRV_PCM_STATE_XRUN || runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG - if (runtime->status->state == SNDRV_PCM_STATE_XRUN) - printk("pcm_oss: read: recovering from XRUN\n"); - else - printk("pcm_oss: read: recovering from SUSPEND\n"); + pcm_dbg(substream->pcm, + "pcm_oss: read: recovering from %s\n", + runtime->status->state == SNDRV_PCM_STATE_XRUN ? + "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) @@ -1202,10 +1233,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); } if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1229,10 +1260,10 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void if (runtime->status->state == SNDRV_PCM_STATE_XRUN || runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG - if (runtime->status->state == SNDRV_PCM_STATE_XRUN) - printk("pcm_oss: writev: recovering from XRUN\n"); - else - printk("pcm_oss: writev: recovering from SUSPEND\n"); + pcm_dbg(substream->pcm, + "pcm_oss: writev: recovering from %s\n", + runtime->status->state == SNDRV_PCM_STATE_XRUN ? + "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); if (ret < 0) @@ -1265,10 +1296,10 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void * if (runtime->status->state == SNDRV_PCM_STATE_XRUN || runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG - if (runtime->status->state == SNDRV_PCM_STATE_XRUN) - printk("pcm_oss: readv: recovering from XRUN\n"); - else - printk("pcm_oss: readv: recovering from SUSPEND\n"); + pcm_dbg(substream->pcm, + "pcm_oss: readv: recovering from %s\n", + runtime->status->state == SNDRV_PCM_STATE_XRUN ? + "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) @@ -1301,7 +1332,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; if (!in_kernel) { - if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes)) + if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes)) return -EFAULT; buf = runtime->oss.buffer; } @@ -1397,7 +1428,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t frames, frames1; #ifdef CONFIG_SND_PCM_OSS_PLUGINS - char __user *final_dst = (char __user *)buf; + char __user *final_dst = (char __force __user *)buf; if (runtime->oss.plugin_first) { struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; @@ -1480,16 +1511,19 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) { struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + int i; - substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; - if (substream != NULL) { - snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); - substream->runtime->oss.prepare = 1; - } - substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; - if (substream != NULL) { + for (i = 0; i < 2; i++) { + substream = pcm_oss_file->streams[i]; + if (!substream) + continue; + runtime = substream->runtime; snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); - substream->runtime->oss.prepare = 1; + runtime->oss.prepare = 1; + runtime->oss.buffer_used = 0; + runtime->oss.prev_hw_ptr_period = 0; + runtime->oss.period_ptr = 0; } return 0; } @@ -1514,6 +1548,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) { struct snd_pcm_runtime *runtime; ssize_t result = 0; + snd_pcm_state_t state; long res; wait_queue_t wait; @@ -1521,7 +1556,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); #ifdef OSS_DEBUG - printk("sync1: size = %li\n", size); + pcm_dbg(substream->pcm, "sync1: size = %li\n", size); #endif while (1) { result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1); @@ -1535,9 +1570,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) result = 0; set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_lock_irq(substream); - res = runtime->status->state; + state = runtime->status->state; snd_pcm_stream_unlock_irq(substream); - if (res != SNDRV_PCM_STATE_RUNNING) { + if (state != SNDRV_PCM_STATE_RUNNING) { set_current_state(TASK_RUNNING); break; } @@ -1547,7 +1582,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) break; } if (res == 0) { - snd_printk(KERN_ERR "OSS sync error - DMA timeout\n"); + pcm_err(substream->pcm, + "OSS sync error - DMA timeout\n"); result = -EIO; break; } @@ -1578,7 +1614,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) mutex_lock(&runtime->oss.params_lock); if (runtime->oss.buffer_used > 0) { #ifdef OSS_DEBUG - printk("sync: buffer_used\n"); + pcm_dbg(substream->pcm, "sync: buffer_used\n"); #endif size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width; snd_pcm_format_set_silence(format, @@ -1591,7 +1627,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) } } else if (runtime->oss.period_ptr > 0) { #ifdef OSS_DEBUG - printk("sync: period_ptr\n"); + pcm_dbg(substream->pcm, "sync: period_ptr\n"); #endif size = runtime->oss.period_bytes - runtime->oss.period_ptr; snd_pcm_format_set_silence(format, @@ -1623,7 +1659,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size1); size1 /= runtime->channels; /* frames */ fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); + snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); snd_leave_user(fs); } } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { @@ -1755,7 +1791,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE | AFMT_S32_LE | AFMT_S32_BE | - AFMT_S24_LE | AFMT_S24_LE | + AFMT_S24_LE | AFMT_S24_BE | AFMT_S24_PACKED; params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) @@ -1764,7 +1800,8 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) err = snd_pcm_hw_refine(substream, params); format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); - snd_assert(err >= 0, return err); + if (err < 0) + return err; for (fmt = 0; fmt < 32; ++fmt) { if (snd_mask_test(&format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); @@ -1882,7 +1919,9 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig static int snd_pcm_oss_nonblock(struct file * file) { + spin_lock(&file->f_lock); file->f_flags |= O_NONBLOCK; + spin_unlock(&file->f_lock); return 0; } @@ -1923,7 +1962,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file) return result; } -static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr) +static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hw_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t appl_ptr; @@ -1939,7 +1979,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr int err, cmd; #ifdef OSS_DEBUG - printk("pcm_oss: trigger = 0x%x\n", trigger); + pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger); #endif psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; @@ -1959,7 +1999,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr if (runtime->oss.trigger) goto _skip1; if (atomic_read(&psubstream->mmap_count)) - snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); + snd_pcm_oss_simulate_fill(psubstream, + get_hw_ptr_period(runtime)); runtime->oss.trigger = 1; runtime->start_threshold = 1; cmd = SNDRV_PCM_IOCTL_START; @@ -2078,11 +2119,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); if (atomic_read(&substream->mmap_count)) { snd_pcm_sframes_t n; - n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; + delay = get_hw_ptr_period(runtime); + n = delay - runtime->oss.prev_hw_ptr_period; if (n < 0) n += runtime->boundary; info.blocks = n / runtime->period_size; - runtime->oss.prev_hw_ptr_interrupt = delay; + runtime->oss.prev_hw_ptr_period = delay; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_pcm_oss_simulate_fill(substream, delay); info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; @@ -2157,7 +2199,9 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre } #ifdef OSS_DEBUG - printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize); + pcm_dbg(substream->pcm, + "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", + info.bytes, info.fragments, info.fragstotal, info.fragsize); #endif if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; @@ -2167,7 +2211,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct buffmem_desc __user * _info) { // it won't be probably implemented - // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n"); + // pr_debug("TODO: snd_pcm_oss_get_mapbuf\n"); return -EINVAL; } @@ -2250,7 +2294,8 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream, static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file) { int cidx; - snd_assert(pcm_oss_file != NULL, return -ENXIO); + if (!pcm_oss_file) + return 0; for (cidx = 0; cidx < 2; ++cidx) { struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx]; if (substream) @@ -2269,10 +2314,10 @@ static int snd_pcm_oss_open_file(struct file *file, int idx, err; struct snd_pcm_oss_file *pcm_oss_file; struct snd_pcm_substream *substream; - unsigned int f_mode = file->f_mode; + fmode_t f_mode = file->f_mode; - snd_assert(rpcm_oss_file != NULL, return -EINVAL); - *rpcm_oss_file = NULL; + if (rpcm_oss_file) + *rpcm_oss_file = NULL; pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL); if (pcm_oss_file == NULL) @@ -2312,7 +2357,8 @@ static int snd_pcm_oss_open_file(struct file *file, } file->private_data = pcm_oss_file; - *rpcm_oss_file = pcm_oss_file; + if (rpcm_oss_file) + *rpcm_oss_file = pcm_oss_file; return 0; } @@ -2321,7 +2367,8 @@ static int snd_task_name(struct task_struct *task, char *name, size_t size) { unsigned int idx; - snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); + if (snd_BUG_ON(!task || !name || size < 2)) + return -EINVAL; for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++) name[idx] = task->comm[idx]; name[idx] = '\0'; @@ -2338,6 +2385,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) int nonblock; wait_queue_t wait; + err = nonseekable_open(inode, file); + if (err < 0) + return err; + pcm = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_PCM); if (pcm == NULL) { @@ -2386,6 +2437,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) mutex_unlock(&pcm->open_mutex); schedule(); mutex_lock(&pcm->open_mutex); + if (pcm->card->shutdown) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; @@ -2395,6 +2450,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) mutex_unlock(&pcm->open_mutex); if (err < 0) goto __error; + snd_card_unref(pcm->card); return err; __error: @@ -2402,6 +2458,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) __error2: snd_card_file_remove(pcm->card, file); __error1: + if (pcm) + snd_card_unref(pcm->card); return err; } @@ -2415,7 +2473,8 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file) substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream == NULL) substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; - snd_assert(substream != NULL, return -ENXIO); + if (snd_BUG_ON(!substream)) + return -ENXIO; pcm = substream->pcm; if (!pcm->card->shutdown) snd_pcm_oss_sync(pcm_oss_file); @@ -2448,14 +2507,15 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long if (substream != NULL) break; } - snd_assert(substream != NULL, return -ENXIO); + if (snd_BUG_ON(idx >= 2)) + return -ENXIO; return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg); } #endif if (((cmd >> 8) & 0xff) != 'P') return -EINVAL; #ifdef OSS_DEBUG - printk("pcm_oss: ioctl = 0x%x\n", cmd); + pr_debug("pcm_oss: ioctl = 0x%x\n", cmd); #endif switch (cmd) { case SNDCTL_DSP_RESET: @@ -2582,7 +2642,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long case SNDCTL_DSP_PROFILE: return 0; /* silently ignore */ default: - snd_printd("pcm_oss: unknown command = 0x%x\n", cmd); + pr_debug("pcm_oss: unknown command = 0x%x\n", cmd); } return -EINVAL; } @@ -2609,7 +2669,9 @@ static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t coun #else { ssize_t res = snd_pcm_oss_read1(substream, buf, count); - printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res); + pcm_dbg(substream->pcm, + "pcm_oss: read %li bytes (returned %li bytes)\n", + (long)count, (long)res); return res; } #endif @@ -2628,7 +2690,8 @@ static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size substream->f_flags = file->f_flags & O_NONBLOCK; result = snd_pcm_oss_write1(substream, buf, count); #ifdef OSS_DEBUG - printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result); + pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n", + (long)count, (long)result); #endif return result; } @@ -2637,18 +2700,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_playback_avail(runtime) >= + runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_capture_avail(runtime) >= + runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) @@ -2702,7 +2769,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) int err; #ifdef OSS_DEBUG - printk("pcm_oss: mmap begin\n"); + pr_debug("pcm_oss: mmap begin\n"); #endif pcm_oss_file = file->private_data; switch ((area->vm_flags & (VM_READ | VM_WRITE))) { @@ -2752,7 +2819,8 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) runtime->silence_threshold = 0; runtime->silence_size = 0; #ifdef OSS_DEBUG - printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes); + pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n", + runtime->oss.mmap_bytes); #endif /* In mmap mode we never stop */ runtime->stop_threshold = runtime->boundary; @@ -2804,7 +2872,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_str *pstr = entry->private_data; - char line[128], str[32], task_name[32], *ptr; + char line[128], str[32], task_name[32]; + const char *ptr; int idx1; struct snd_pcm_oss_setup *setup, *setup1, template; @@ -2854,7 +2923,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, setup = kmalloc(sizeof(*setup), GFP_KERNEL); if (! setup) { buffer->error = -ENOMEM; - mutex_lock(&pstr->oss.setup_mutex); + mutex_unlock(&pstr->oss.setup_mutex); return; } if (pstr->oss.setup_list == NULL) @@ -2868,7 +2937,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, if (! template.task_name) { kfree(setup); buffer->error = -ENOMEM; - mutex_lock(&pstr->oss.setup_mutex); + mutex_unlock(&pstr->oss.setup_mutex); return; } } @@ -2926,6 +2995,7 @@ static const struct file_operations snd_pcm_oss_f_reg = .write = snd_pcm_oss_write, .open = snd_pcm_oss_open, .release = snd_pcm_oss_release, + .llseek = no_llseek, .poll = snd_pcm_oss_poll, .unlocked_ioctl = snd_pcm_oss_ioctl, .compat_ioctl = snd_pcm_oss_ioctl_compat, @@ -2934,12 +3004,10 @@ static const struct file_operations snd_pcm_oss_f_reg = static void register_oss_dsp(struct snd_pcm *pcm, int index) { - char name[128]; - sprintf(name, "dsp%i%i", pcm->card->number, pcm->device); if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, pcm->card, index, &snd_pcm_oss_f_reg, - pcm, name) < 0) { - snd_printk(KERN_ERR "unable to register OSS PCM device %i:%i\n", + pcm) < 0) { + pcm_err(pcm, "unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device); } } @@ -3020,12 +3088,12 @@ static int __init alsa_pcm_oss_init(void) /* check device map table */ for (i = 0; i < SNDRV_CARDS; i++) { if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) { - snd_printk(KERN_ERR "invalid dsp_map[%d] = %d\n", + pr_err("ALSA: pcm_oss: invalid dsp_map[%d] = %d\n", i, dsp_map[i]); dsp_map[i] = 0; } if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) { - snd_printk(KERN_ERR "invalid adsp_map[%d] = %d\n", + pr_err("ALSA: pcm_oss: invalid adsp_map[%d] = %d\n", i, adsp_map[i]); adsp_map[i] = 1; } diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index bec94138205..727ac44d39f 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -62,7 +62,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t if ((width = snd_pcm_format_physical_width(format->format)) < 0) return width; size = frames * format->channels * width; - snd_assert((size % 8) == 0, return -ENXIO); + if (snd_BUG_ON(size % 8)) + return -ENXIO; size /= 8; if (plugin->buf_frames < frames) { vfree(plugin->buf); @@ -84,7 +85,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t c->area.step = format->channels * width; } } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { - snd_assert((size % format->channels) == 0,); + if (snd_BUG_ON(size % format->channels)) + return -EINVAL; size /= format->channels; for (channel = 0; channel < format->channels; channel++, c++) { c->frames = frames; @@ -102,13 +104,15 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) { int err; - snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); + if (snd_BUG_ON(!snd_pcm_plug_first(plug))) + return -ENXIO; if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug); while (plugin->next) { if (plugin->dst_frames) frames = plugin->dst_frames(plugin, frames); - snd_assert(frames > 0, return -ENXIO); + if (snd_BUG_ON(frames <= 0)) + return -ENXIO; plugin = plugin->next; err = snd_pcm_plugin_alloc(plugin, frames); if (err < 0) @@ -119,7 +123,8 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) while (plugin->prev) { if (plugin->src_frames) frames = plugin->src_frames(plugin, frames); - snd_assert(frames > 0, return -ENXIO); + if (snd_BUG_ON(frames <= 0)) + return -ENXIO; plugin = plugin->prev; err = snd_pcm_plugin_alloc(plugin, frames); if (err < 0) @@ -148,8 +153,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug, struct snd_pcm_plugin *plugin; unsigned int channels; - snd_assert(plug != NULL, return -ENXIO); - snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); + if (snd_BUG_ON(!plug)) + return -ENXIO; + if (snd_BUG_ON(!src_format || !dst_format)) + return -ENXIO; plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL); if (plugin == NULL) return -ENOMEM; @@ -159,10 +166,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug, plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; plugin->src_format = *src_format; plugin->src_width = snd_pcm_format_physical_width(src_format->format); - snd_assert(plugin->src_width > 0, ); + snd_BUG_ON(plugin->src_width <= 0); plugin->dst_format = *dst_format; plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); - snd_assert(plugin->dst_width > 0, ); + snd_BUG_ON(plugin->dst_width <= 0); if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) channels = src_format->channels; else @@ -192,11 +199,13 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin) snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) { struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; - int stream = snd_pcm_plug_stream(plug); + int stream; - snd_assert(plug != NULL, return -ENXIO); + if (snd_BUG_ON(!plug)) + return -ENXIO; if (drv_frames == 0) return 0; + stream = snd_pcm_plug_stream(plug); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { plugin = snd_pcm_plug_last(plug); while (plugin && drv_frames > 0) { @@ -222,12 +231,14 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc { struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; snd_pcm_sframes_t frames; - int stream = snd_pcm_plug_stream(plug); + int stream; - snd_assert(plug != NULL, return -ENXIO); + if (snd_BUG_ON(!plug)) + return -ENXIO; if (clt_frames == 0) return 0; frames = clt_frames; + stream = snd_pcm_plug_stream(plug); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { plugin = snd_pcm_plug_first(plug); while (plugin && frames > 0) { @@ -255,7 +266,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, int format) +static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -267,16 +278,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format) SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & (u32)linfmts) formats.bits[0] |= (u32)linfmts; if (formats.bits[1] & (u32)(linfmts >> 32)) formats.bits[1] |= (u32)(linfmts >> 32); - return snd_mask_test(&formats, format); + return snd_mask_test(&formats, (__force int)format); } -static int preferred_formats[] = { +static snd_pcm_format_t preferred_formats[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, @@ -297,24 +308,25 @@ static int preferred_formats[] = { SNDRV_PCM_FORMAT_U8 }; -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask) { int i; - if (snd_mask_test(format_mask, format)) + if (snd_mask_test(format_mask, (__force int)format)) return format; - if (! snd_pcm_plug_formats(format_mask, format)) - return -EINVAL; + if (!snd_pcm_plug_formats(format_mask, format)) + return (__force snd_pcm_format_t)-EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - int best_format = -1; + snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { - int f = preferred_formats[i]; + snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, f)) + if (!snd_mask_test(format_mask, (__force int)f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -328,17 +340,20 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) best = badness; } } - return best_format >= 0 ? best_format : -EINVAL; + if ((__force int)best_format >= 0) + return best_format; + else + return (__force snd_pcm_format_t)-EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { - int format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, format1)) + snd_pcm_format_t format1 = preferred_formats[i]; + if (snd_mask_test(format_mask, (__force int)format1)) return format1; } default: - return -EINVAL; + return (__force snd_pcm_format_t)-EINVAL; } } } @@ -350,7 +365,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format tmpformat; struct snd_pcm_plugin_format dstformat; struct snd_pcm_plugin_format srcformat; - int src_access, dst_access; + snd_pcm_access_t src_access, dst_access; struct snd_pcm_plugin *plugin = NULL; int err; int stream = snd_pcm_plug_stream(plug); @@ -540,7 +555,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu int width, nchannels, channel; int stream = snd_pcm_plug_stream(plug); - snd_assert(buf != NULL, return -ENXIO); + if (snd_BUG_ON(!buf)) + return -ENXIO; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { plugin = snd_pcm_plug_first(plug); format = &plugin->src_format; @@ -553,7 +569,9 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu if ((width = snd_pcm_format_physical_width(format->format)) < 0) return width; nchannels = format->channels; - snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); + if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + format->channels > 1)) + return -ENXIO; for (channel = 0; channel < nchannels; channel++, v++) { v->frames = count; v->enabled = 1; @@ -629,7 +647,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str } int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ unsigned char *dst; @@ -676,7 +694,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ char *src, *dst; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index ca2f4c39be4..a5035c2369a 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel { }; struct snd_pcm_plugin_format { - int format; + snd_pcm_format_t format; unsigned int rate; unsigned int channels; }; @@ -58,7 +58,7 @@ struct snd_pcm_plugin { struct snd_pcm_plugin_format dst_format; /* destination format */ int src_width; /* sample width in bits */ int dst_width; /* sample width in bits */ - int access; + snd_pcm_access_t access; snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames); snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames); snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin, @@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *slave_params); -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask); +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, size_t src_offset, const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size); void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr); @@ -176,9 +177,9 @@ static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_ #endif #ifdef PLUGIN_DEBUG -#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args) +#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args) #else -#define pdprintf( fmt, args... ) +#define pdprintf(fmt, args...) #endif #endif /* __PCM_PLUGIN_H */ diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 14dfb3175d8..2fa9299a440 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -157,7 +157,7 @@ static void resample_shrink(struct snd_pcm_plugin *plugin, while (dst_frames1 > 0) { S1 = S2; if (src_frames1-- > 0) { - S1 = *src; + S2 = *src; src += src_step; } if (pos & ~R_MASK) { @@ -185,7 +185,8 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_ struct rate_priv *data; snd_pcm_sframes_t res; - snd_assert(plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin)) + return -ENXIO; if (frames == 0) return 0; data = (struct rate_priv *)plugin->extra_data; @@ -217,7 +218,8 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_ struct rate_priv *data; snd_pcm_sframes_t res; - snd_assert(plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin)) + return -ENXIO; if (frames == 0) return 0; data = (struct rate_priv *)plugin->extra_data; @@ -252,19 +254,20 @@ static snd_pcm_sframes_t rate_transfer(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames; struct rate_priv *data; - snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) + return -ENXIO; if (frames == 0) return 0; #ifdef CONFIG_SND_DEBUG { unsigned int channel; for (channel = 0; channel < plugin->src_format.channels; channel++) { - snd_assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0, - return -ENXIO); - snd_assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0, - return -ENXIO); + if (snd_BUG_ON(src_channels[channel].area.first % 8 || + src_channels[channel].area.step % 8)) + return -ENXIO; + if (snd_BUG_ON(dst_channels[channel].area.first % 8 || + dst_channels[channel].area.step % 8)) + return -ENXIO; } } #endif @@ -281,7 +284,8 @@ static int rate_action(struct snd_pcm_plugin *plugin, enum snd_pcm_plugin_action action, unsigned long udata) { - snd_assert(plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin)) + return -ENXIO; switch (action) { case INIT: case PREPARE: @@ -302,14 +306,20 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug, struct rate_priv *data; struct snd_pcm_plugin *plugin; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(src_format->channels == dst_format->channels, return -ENXIO); - snd_assert(src_format->channels > 0, return -ENXIO); - snd_assert(src_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO); - snd_assert(dst_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO); - snd_assert(src_format->rate != dst_format->rate, return -ENXIO); + if (snd_BUG_ON(src_format->channels != dst_format->channels)) + return -ENXIO; + if (snd_BUG_ON(src_format->channels <= 0)) + return -ENXIO; + if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16)) + return -ENXIO; + if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16)) + return -ENXIO; + if (snd_BUG_ON(src_format->rate == dst_format->rate)) + return -ENXIO; err = snd_pcm_plugin_build(plug, "rate conversion", src_format, dst_format, diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index da7ab7a3e82..c8171f5783c 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -19,14 +19,13 @@ * */ -#include <linux/slab.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> #include "pcm_plugin.h" static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { int dst = 0; for (; dst < ndsts; ++dst) { @@ -39,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel, struct snd_pcm_plugin_channel *dst_channel, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { dst_channel->enabled = 1; snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format); @@ -52,9 +51,10 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin, { int nsrcs, ndsts, dst; struct snd_pcm_plugin_channel *dvp; - int format; + snd_pcm_format_t format; - snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) + return -ENXIO; if (frames == 0) return 0; @@ -90,10 +90,13 @@ int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug, struct snd_pcm_plugin *plugin; int err; - snd_assert(r_plugin != NULL, return -ENXIO); + if (snd_BUG_ON(!r_plugin)) + return -ENXIO; *r_plugin = NULL; - snd_assert(src_format->rate == dst_format->rate, return -ENXIO); - snd_assert(src_format->format == dst_format->format, return -ENXIO); + if (snd_BUG_ON(src_format->rate != dst_format->rate)) + return -ENXIO; + if (snd_BUG_ON(src_format->format != dst_format->format)) + return -ENXIO; err = snd_pcm_plugin_build(plug, "route conversion", src_format, dst_format, 0, &plugin); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 9dd9bc73fe1..43932e8dce6 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -21,8 +21,10 @@ #include <linux/init.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/device.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/pcm.h> @@ -42,17 +44,52 @@ static int snd_pcm_dev_free(struct snd_device *device); static int snd_pcm_dev_register(struct snd_device *device); static int snd_pcm_dev_disconnect(struct snd_device *device); -static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) +static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) { struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { + if (pcm->internal) + continue; if (pcm->card == card && pcm->device == device) return pcm; } return NULL; } +static int snd_pcm_next(struct snd_card *card, int device) +{ + struct snd_pcm *pcm; + + list_for_each_entry(pcm, &snd_pcm_devices, list) { + if (pcm->internal) + continue; + if (pcm->card == card && pcm->device > device) + return pcm->device; + else if (pcm->card->number > card->number) + return -1; + } + return -1; +} + +static int snd_pcm_add(struct snd_pcm *newpcm) +{ + struct snd_pcm *pcm; + + list_for_each_entry(pcm, &snd_pcm_devices, list) { + if (pcm->card == newpcm->card && pcm->device == newpcm->device) + return -EBUSY; + if (pcm->card->number > newpcm->card->number || + (pcm->card == newpcm->card && + pcm->device > newpcm->device)) { + list_add(&newpcm->list, pcm->list.prev); + return 0; + } + } + list_add_tail(&newpcm->list, &snd_pcm_devices); + return 0; +} + static int snd_pcm_control_ioctl(struct snd_card *card, struct snd_ctl_file *control, unsigned int cmd, unsigned long arg) @@ -65,14 +102,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)arg)) return -EFAULT; mutex_lock(®ister_mutex); - device = device < 0 ? 0 : device + 1; - while (device < SNDRV_PCM_DEVICES) { - if (snd_pcm_search(card, device)) - break; - device++; - } - if (device == SNDRV_PCM_DEVICES) - device = -1; + device = snd_pcm_next(card, device); mutex_unlock(®ister_mutex); if (put_user(device, (int __user *)arg)) return -EFAULT; @@ -98,7 +128,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, if (get_user(subdevice, &info->subdevice)) return -EFAULT; mutex_lock(®ister_mutex); - pcm = snd_pcm_search(card, device); + pcm = snd_pcm_get(card, device); if (pcm == NULL) { err = -ENXIO; goto _error; @@ -138,18 +168,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, return -ENOIOCTLCMD; } -#ifdef CONFIG_SND_VERBOSE_PROCFS - -#define STATE(v) [SNDRV_PCM_STATE_##v] = #v -#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v -#define READY(v) [SNDRV_PCM_READY_##v] = #v -#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v -#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v -#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v -#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v -#define START(v) [SNDRV_PCM_START_##v] = #v #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v -#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v static char *snd_pcm_format_names[] = { FORMAT(S8), @@ -190,12 +209,33 @@ static char *snd_pcm_format_names[] = { FORMAT(S18_3BE), FORMAT(U18_3LE), FORMAT(U18_3BE), + FORMAT(G723_24), + FORMAT(G723_24_1B), + FORMAT(G723_40), + FORMAT(G723_40_1B), + FORMAT(DSD_U8), + FORMAT(DSD_U16_LE), }; -static const char *snd_pcm_format_name(snd_pcm_format_t format) +const char *snd_pcm_format_name(snd_pcm_format_t format) { - return snd_pcm_format_names[format]; + if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) + return "Unknown"; + return snd_pcm_format_names[(__force unsigned int)format]; } +EXPORT_SYMBOL_GPL(snd_pcm_format_name); + +#ifdef CONFIG_SND_VERBOSE_PROCFS + +#define STATE(v) [SNDRV_PCM_STATE_##v] = #v +#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v +#define READY(v) [SNDRV_PCM_READY_##v] = #v +#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v +#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v +#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v +#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v +#define START(v) [SNDRV_PCM_START_##v] = #v +#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v static char *snd_pcm_stream_names[] = { STREAM(PLAYBACK), @@ -232,32 +272,30 @@ static char *snd_pcm_tstamp_mode_names[] = { static const char *snd_pcm_stream_name(int stream) { - snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL); return snd_pcm_stream_names[stream]; } static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[access]; + return snd_pcm_access_names[(__force int)access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[subformat]; + return snd_pcm_subformat_names[(__force int)subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) { - snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL); return snd_pcm_tstamp_mode_names[mode]; } static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[state]; + return snd_pcm_state_names[(__force int)state]; } -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) #include <linux/soundcard.h> static const char *snd_pcm_oss_format_name(int format) @@ -300,7 +338,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) { - printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); + pcm_dbg(substream->pcm, + "snd_pcm_proc_info_read: cannot malloc\n"); return; } @@ -334,22 +373,24 @@ static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data, - buffer); + snd_pcm_proc_info_read(entry->private_data, buffer); } static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - return; + goto unlock; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); @@ -358,7 +399,7 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) if (substream->oss.oss) { snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); @@ -368,20 +409,25 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - return; + goto unlock; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); @@ -391,26 +437,32 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; struct snd_pcm_status status; int err; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } memset(&status, 0, sizeof(status)); err = snd_pcm_status(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - return; + goto unlock; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); + snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); snd_iprintf(buffer, "tstamp : %ld.%09ld\n", @@ -421,6 +473,8 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, snd_iprintf(buffer, "-----\n"); snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); + unlock: + mutex_unlock(&substream->pcm->open_mutex); } #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -590,7 +644,7 @@ static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substrea * calling this, i.e. zero must be given to the argument of * snd_pcm_new(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) { @@ -598,16 +652,16 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) struct snd_pcm_str *pstr = &pcm->streams[stream]; struct snd_pcm_substream *substream, *prev; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) mutex_init(&pstr->oss.setup_mutex); #endif pstr->stream = stream; pstr->pcm = pcm; pstr->substream_count = substream_count; - if (substream_count > 0) { + if (substream_count > 0 && !pcm->internal) { err = snd_pcm_stream_proc_init(pstr); if (err < 0) { - snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); + pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); return err; } } @@ -615,7 +669,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) for (idx = 0, prev = NULL; idx < substream_count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); if (substream == NULL) { - snd_printk(KERN_ERR "Cannot allocate PCM substream\n"); + pcm_err(pcm, "Cannot allocate PCM substream\n"); return -ENOMEM; } substream->pcm = pcm; @@ -623,29 +677,29 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) substream->number = idx; substream->stream = stream; sprintf(substream->name, "subdevice #%i", idx); - snprintf(substream->latency_id, sizeof(substream->latency_id), - "ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device, - (stream ? 'c' : 'p'), idx); substream->buffer_bytes_max = UINT_MAX; if (prev == NULL) pstr->substream = substream; else prev->next = substream; - err = snd_pcm_substream_proc_init(substream); - if (err < 0) { - snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); - if (prev == NULL) - pstr->substream = NULL; - else - prev->next = NULL; - kfree(substream); - return err; + + if (!pcm->internal) { + err = snd_pcm_substream_proc_init(substream); + if (err < 0) { + pcm_err(pcm, + "Error in snd_pcm_stream_proc_init\n"); + if (prev == NULL) + pstr->substream = NULL; + else + prev->next = NULL; + kfree(substream); + return err; + } } substream->group = &substream->self_group; spin_lock_init(&substream->self_group.lock); INIT_LIST_HEAD(&substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams); - spin_lock_init(&substream->timer_lock); atomic_set(&substream->mmap_count, 0); prev = substream; } @@ -654,25 +708,9 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) EXPORT_SYMBOL(snd_pcm_new_stream); -/** - * snd_pcm_new - create a new PCM instance - * @card: the card instance - * @id: the id string - * @device: the device index (zero based) - * @playback_count: the number of substreams for playback - * @capture_count: the number of substreams for capture - * @rpcm: the pointer to store the new pcm instance - * - * Creates a new PCM instance. - * - * The pcm operators have to be set afterwards to the new instance - * via snd_pcm_set_ops(). - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_new(struct snd_card *card, char *id, int device, - int playback_count, int capture_count, - struct snd_pcm ** rpcm) +static int _snd_pcm_new(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, bool internal, + struct snd_pcm **rpcm) { struct snd_pcm *pcm; int err; @@ -682,16 +720,18 @@ int snd_pcm_new(struct snd_card *card, char *id, int device, .dev_disconnect = snd_pcm_dev_disconnect, }; - snd_assert(rpcm != NULL, return -EINVAL); - *rpcm = NULL; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; + if (rpcm) + *rpcm = NULL; pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); if (pcm == NULL) { - snd_printk(KERN_ERR "Cannot allocate PCM\n"); + dev_err(card->dev, "Cannot allocate PCM\n"); return -ENOMEM; } pcm->card = card; pcm->device = device; + pcm->internal = internal; if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { @@ -708,16 +748,68 @@ int snd_pcm_new(struct snd_card *card, char *id, int device, snd_pcm_free(pcm); return err; } - *rpcm = pcm; + if (rpcm) + *rpcm = pcm; return 0; } +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_new(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, struct snd_pcm **rpcm) +{ + return _snd_pcm_new(card, id, device, playback_count, capture_count, + false, rpcm); +} EXPORT_SYMBOL(snd_pcm_new); +/** + * snd_pcm_new_internal - create a new internal PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based - shared with normal PCMs) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new internal PCM instance with no userspace device or procfs + * entries. This is used by ASoC Back End PCMs in order to create a PCM that + * will only be used internally by kernel drivers. i.e. it cannot be opened + * by userspace. It provides existing ASoC components drivers with a substream + * and access to any private data. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, + struct snd_pcm **rpcm) +{ + return _snd_pcm_new(card, id, device, playback_count, capture_count, + true, rpcm); +} +EXPORT_SYMBOL(snd_pcm_new_internal); + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) struct snd_pcm_oss_setup *setup, *setupn; #endif substream = pstr->substream; @@ -729,7 +821,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) substream = substream_next; } snd_pcm_stream_proc_done(pstr); -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) for (setup = pstr->oss.setup_list; setup; setup = setupn) { setupn = setup->next; kfree(setup->task_name); @@ -742,7 +834,8 @@ static int snd_pcm_free(struct snd_pcm *pcm) { struct snd_pcm_notify *notify; - snd_assert(pcm != NULL, return -ENXIO); + if (!pcm) + return 0; list_for_each_entry(notify, &snd_pcm_notify_list, list) { notify->n_unregister(pcm); } @@ -773,23 +866,23 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, int prefer_subdevice = -1; size_t size; - snd_assert(rsubstream != NULL, return -EINVAL); + if (snd_BUG_ON(!pcm || !rsubstream)) + return -ENXIO; *rsubstream = NULL; - snd_assert(pcm != NULL, return -ENXIO); pstr = &pcm->streams[stream]; if (pstr->substream == NULL || pstr->substream_count == 0) return -ENODEV; card = pcm->card; - down_read(&card->controls_rwsem); + read_lock(&card->ctl_files_rwlock); list_for_each_entry(kctl, &card->ctl_files, list) { - if (kctl->pid == current->pid) { + if (kctl->pid == task_pid(current)) { prefer_subdevice = kctl->prefer_pcm_subdevice; if (prefer_subdevice != -1) break; } } - up_read(&card->controls_rwsem); + read_unlock(&card->ctl_files_rwlock); switch (stream) { case SNDRV_PCM_STREAM_PLAYBACK: @@ -867,6 +960,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, memset((void*)runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->tsleep); runtime->status->state = SNDRV_PCM_STATE_OPEN; @@ -874,6 +968,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, substream->private_data = pcm->private_data; substream->ref_count = 1; substream->f_flags = file->f_flags; + substream->pid = get_pid(task_pid(current)); pstr->substream_opened++; *rsubstream = substream; return 0; @@ -883,8 +978,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; + if (PCM_RUNTIME_CHECK(substream)) + return; runtime = substream->runtime; - snd_assert(runtime != NULL, return); if (runtime->private_free != NULL) runtime->private_free(runtime); snd_free_pages((void*)runtime->status, @@ -892,8 +988,13 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + kfree(runtime->hwptr_log); +#endif kfree(runtime); substream->runtime = NULL; + put_pid(substream->pid); + substream->pid = NULL; substream->pstr->substream_opened--; } @@ -917,8 +1018,20 @@ static ssize_t show_pcm_class(struct device *dev, return snprintf(buf, PAGE_SIZE, "%s\n", str); } -static struct device_attribute pcm_attrs = - __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static struct attribute *pcm_dev_attrs[] = { + &dev_attr_pcm_class.attr, + NULL +}; + +static struct attribute_group pcm_dev_attr_group = { + .attrs = pcm_dev_attrs, +}; + +static const struct attribute_group *pcm_dev_attr_groups[] = { + &pcm_dev_attr_group, + NULL +}; static int snd_pcm_dev_register(struct snd_device *device) { @@ -926,19 +1039,21 @@ static int snd_pcm_dev_register(struct snd_device *device) struct snd_pcm_substream *substream; struct snd_pcm_notify *notify; char str[16]; - struct snd_pcm *pcm = device->device_data; + struct snd_pcm *pcm; struct device *dev; - snd_assert(pcm != NULL && device != NULL, return -ENXIO); + if (snd_BUG_ON(!device || !device->device_data)) + return -ENXIO; + pcm = device->device_data; mutex_lock(®ister_mutex); - if (snd_pcm_search(pcm->card, pcm->device)) { + err = snd_pcm_add(pcm); + if (err) { mutex_unlock(®ister_mutex); - return -EBUSY; + return err; } - list_add_tail(&pcm->list, &snd_pcm_devices); for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; - if (pcm->streams[cidx].substream == NULL) + if (pcm->streams[cidx].substream == NULL || pcm->internal) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: @@ -966,8 +1081,18 @@ static int snd_pcm_dev_register(struct snd_device *device) mutex_unlock(®ister_mutex); return err; } - snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, - &pcm_attrs); + + dev = snd_get_device(devtype, pcm->card, pcm->device); + if (dev) { + err = sysfs_create_groups(&dev->kobj, + pcm_dev_attr_groups); + if (err < 0) + dev_warn(dev, + "pcm %d:%d: cannot create sysfs groups\n", + pcm->card->number, pcm->device); + put_device(dev); + } + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } @@ -990,11 +1115,19 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) if (list_empty(&pcm->list)) goto unlock; + mutex_lock(&pcm->open_mutex); + wake_up(&pcm->open_wait); list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) - for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) - if (substream->runtime) + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime) { substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; + wake_up(&substream->runtime->sleep); + wake_up(&substream->runtime->tsleep); + } + snd_pcm_stream_unlock_irq(substream); + } list_for_each_entry(notify, &snd_pcm_notify_list, list) { notify->n_disconnect(pcm); } @@ -1009,7 +1142,12 @@ 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; + } } + mutex_unlock(&pcm->open_mutex); unlock: mutex_unlock(®ister_mutex); return 0; @@ -1019,10 +1157,11 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { struct snd_pcm *pcm; - snd_assert(notify != NULL && - notify->n_register != NULL && - notify->n_unregister != NULL && - notify->n_disconnect, return -EINVAL); + if (snd_BUG_ON(!notify || + !notify->n_register || + !notify->n_unregister || + !notify->n_disconnect)) + return -EINVAL; mutex_lock(®ister_mutex); if (nfree) { list_del(¬ify->list); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 49aa693fba8..af49721ba0e 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -21,6 +21,7 @@ /* This file included from pcm_native.c */ #include <linux/compat.h> +#include <linux/slab.h> static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream, s32 __user *src) @@ -189,7 +190,9 @@ struct snd_pcm_status32 { u32 avail_max; u32 overrange; s32 suspended_state; - unsigned char reserved[60]; + u32 reserved_alignment; + struct compat_timespec audio_tstamp; + unsigned char reserved[56-sizeof(struct compat_timespec)]; } __attribute__((packed)); @@ -204,17 +207,16 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, return err; if (put_user(status.state, &src->state) || - put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) || - put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) || - put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || - put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || + compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || + compat_put_timespec(&status.tstamp, &src->tstamp) || put_user(status.appl_ptr, &src->appl_ptr) || put_user(status.hw_ptr, &src->hw_ptr) || put_user(status.delay, &src->delay) || put_user(status.avail, &src->avail) || put_user(status.avail_max, &src->avail_max) || put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state)) + put_user(status.suspended_state, &src->suspended_state) || + compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp)) return -EFAULT; return err; @@ -232,14 +234,11 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, if (! (runtime = substream->runtime)) return -ENOTTY; - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - return -ENOMEM; /* only fifo_size is different, so just copy all */ - if (copy_from_user(data, data32, sizeof(*data32))) { - err = -EFAULT; - goto error; - } + data = memdup_user(data32, sizeof(*data32)); + if (IS_ERR(data)) + return PTR_ERR(data); + if (refine) err = snd_pcm_hw_refine(substream, data); else @@ -344,7 +343,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, kfree(bufs); return -EFAULT; } - bufs[ch] = compat_ptr(ptr); + bufs[i] = compat_ptr(ptr); bufptr++; } if (dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -366,6 +365,7 @@ struct snd_pcm_mmap_status32 { u32 hw_ptr; struct compat_timespec tstamp; s32 suspended_state; + struct compat_timespec audio_tstamp; } __attribute__((packed)); struct snd_pcm_mmap_control32 { @@ -397,7 +397,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, snd_pcm_uframes_t boundary; int err; - snd_assert(runtime, return -EINVAL); + if (snd_BUG_ON(!runtime)) + return -EINVAL; if (get_user(sflags, &src->flags) || get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || @@ -427,12 +428,14 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, sstatus.hw_ptr = status->hw_ptr % boundary; sstatus.tstamp = status->tstamp; sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) || + compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || put_user(sstatus.suspended_state, &src->s.status.suspended_state) || + compat_put_timespec(&sstatus.audio_tstamp, + &src->s.status.audio_tstamp) || put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || put_user(scontrol.avail_min, &src->c.control.avail_min)) return -EFAULT; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c new file mode 100644 index 00000000000..76cbb9ec953 --- /dev/null +++ b/sound/core/pcm_dmaengine.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2012, Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Based on: + * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> + * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. + * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> + * Copyright (C) 2006 Applied Data Systems + * + * 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. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/dmaengine.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <sound/dmaengine_pcm.h> + +struct dmaengine_pcm_runtime_data { + struct dma_chan *dma_chan; + dma_cookie_t cookie; + + unsigned int pos; +}; + +static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( + const struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + return prtd->dma_chan; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); + +/** + * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config + * @substream: PCM substream + * @params: hw_params + * @slave_config: DMA slave config + * + * This function can be used to initialize a dma_slave_config from a substream + * and hw_params in a dmaengine based PCM driver implementation. + */ +int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, + const struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + enum dma_slave_buswidth buswidth; + int bits; + + bits = snd_pcm_format_physical_width(params_format(params)); + if (bits < 8 || bits > 64) + return -EINVAL; + else if (bits == 8) + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (bits == 16) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else if (bits <= 32) + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config->direction = DMA_MEM_TO_DEV; + slave_config->dst_addr_width = buswidth; + } else { + slave_config->direction = DMA_DEV_TO_MEM; + slave_config->src_addr_width = buswidth; + } + + slave_config->device_fc = false; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); + +/** + * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config + * using DAI DMA data. + * @substream: PCM substream + * @dma_data: DAI DMA data + * @slave_config: DMA slave configuration + * + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and + * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * data struct. The src and dst fields will be initialized depending on the + * direction of the substream. If the substream is a playback stream the dst + * fields will be initialized, if it is a capture stream the src fields will be + * initialized. The {dst,src}_addr_width field will only be initialized if the + * addr_width field of the DAI DMA data struct is not equal to + * DMA_SLAVE_BUSWIDTH_UNDEFINED. + */ +void snd_dmaengine_pcm_set_config_from_dai_data( + const struct snd_pcm_substream *substream, + const struct snd_dmaengine_dai_dma_data *dma_data, + struct dma_slave_config *slave_config) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config->dst_addr = dma_data->addr; + slave_config->dst_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->dst_addr_width = dma_data->addr_width; + } else { + slave_config->src_addr = dma_data->addr; + slave_config->src_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->src_addr_width = dma_data->addr_width; + } + + slave_config->slave_id = dma_data->slave_id; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); + +static void dmaengine_pcm_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + prtd->pos += snd_pcm_lib_period_bytes(substream); + if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) + prtd->pos = 0; + + snd_pcm_period_elapsed(substream); +} + +static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_chan *chan = prtd->dma_chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK; + + direction = snd_pcm_substream_to_dma_direction(substream); + + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + prtd->pos = 0; + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), direction, flags); + + if (!desc) + return -ENOMEM; + + desc->callback = dmaengine_pcm_dma_complete; + desc->callback_param = substream; + prtd->cookie = dmaengine_submit(desc); + + return 0; +} + +/** + * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation + * @substream: PCM substream + * @cmd: Trigger command + * + * Returns 0 on success, a negative error code otherwise. + * + * This function can be used as the PCM trigger callback for dmaengine based PCM + * driver implementations. + */ +int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = dmaengine_pcm_prepare_and_submit(substream); + if (ret) + return ret; + dma_async_issue_pending(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dmaengine_resume(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + dmaengine_pause(prtd->dma_chan); + else + dmaengine_terminate_all(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dmaengine_pause(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_STOP: + dmaengine_terminate_all(prtd->dma_chan); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); + +/** + * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function is deprecated and should not be used by new drivers, as its + * results may be unreliable. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + return bytes_to_frames(substream->runtime, prtd->pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); + +/** + * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function can be used as the PCM pointer callback for dmaengine based PCM + * driver implementations. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return bytes_to_frames(substream->runtime, pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); + +/** + * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns NULL or the requested DMA channel. + * + * This function request a DMA channel for usage with dmaengine PCM. + */ +struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, + void *filter_data) +{ + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_CYCLIC, mask); + + return dma_request_channel(mask, filter_fn, filter_data); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); + +/** + * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream + * @substream: PCM substream + * @chan: DMA channel to use for data transfers + * + * Returns 0 on success, a negative error code otherwise. + * + * The function should usually be called from the pcm open callback. Note that + * this function will use private_data field of the substream's runtime. So it + * is not availabe to your pcm driver implementation. + */ +int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, + struct dma_chan *chan) +{ + struct dmaengine_pcm_runtime_data *prtd; + int ret; + + if (!chan) + return -ENXIO; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->dma_chan = chan; + + substream->runtime->private_data = prtd; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); + +/** + * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel + * @substream: PCM substream + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns 0 on success, a negative error code otherwise. + * + * This function will request a DMA channel using the passed filter function and + * data. The function should usually be called from the pcm open callback. Note + * that this function will use private_data field of the substream's runtime. So + * it is not availabe to your pcm driver implementation. + */ +int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data) +{ + return snd_dmaengine_pcm_open(substream, + snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); + +/** + * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream + * @substream: PCM substream + */ +int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + kfree(prtd); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); + +/** + * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel + * @substream: PCM substream + * + * Releases the DMA channel associated with the PCM substream. + */ +int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dma_release_channel(prtd->dma_chan); + + return snd_dmaengine_pcm_close(substream); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); + +MODULE_LICENSE("GPL"); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1533f0379e9..9acc77eae48 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -22,8 +22,11 @@ #include <linux/slab.h> #include <linux/time.h> +#include <linux/math64.h> +#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> @@ -66,6 +69,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } else { if (new_hw_ptr == ULONG_MAX) { /* initialization */ snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); + if (avail > runtime->buffer_size) + avail = runtime->buffer_size; runtime->silence_filled = avail > 0 ? avail : 0; runtime->silence_start = (runtime->status->hw_ptr + runtime->silence_filled) % @@ -85,7 +90,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } frames = runtime->buffer_size - runtime->silence_filled; } - snd_assert(frames <= runtime->buffer_size, return); + if (snd_BUG_ON(frames > runtime->buffer_size)) + return; if (frames == 0) return; ofs = runtime->silence_start % runtime->buffer_size; @@ -96,7 +102,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram if (substream->ops->silence) { int err; err = substream->ops->silence(substream, -1, ofs, transfer); - snd_assert(err >= 0, ); + snd_BUG_ON(err < 0); } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); @@ -108,7 +114,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram for (c = 0; c < channels; ++c) { int err; err = substream->ops->silence(substream, c, ofs, transfer); - snd_assert(err >= 0, ); + snd_BUG_ON(err < 0); } } else { size_t dma_csize = runtime->dma_bytes / channels; @@ -124,42 +130,152 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } +#ifdef CONFIG_SND_DEBUG +void snd_pcm_debug_name(struct snd_pcm_substream *substream, + char *name, size_t len) +{ + snprintf(name, len, "pcmC%dD%d%c:%d", + substream->pcm->card->number, + substream->pcm->device, + substream->stream ? 'c' : 'p', + substream->number); +} +EXPORT_SYMBOL(snd_pcm_debug_name); +#endif + +#define XRUN_DEBUG_BASIC (1<<0) +#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ +#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ +#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ +#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ +#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ +#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ + +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + +#define xrun_debug(substream, mask) \ + ((substream)->pstr->xrun_debug & (mask)) +#else +#define xrun_debug(substream, mask) 0 +#endif + +#define dump_stack_on_xrun(substream) do { \ + if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ + dump_stack(); \ + } while (0) + static void xrun(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; + + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - if (substream->pstr->xrun_debug) { - snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", - substream->pcm->card->number, - substream->pcm->device, - substream->stream ? 'c' : 'p'); - if (substream->pstr->xrun_debug > 1) - dump_stack(); + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); + pcm_warn(substream->pcm, "XRUN: %s\n", name); + dump_stack_on_xrun(substream); } -#endif } -static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) -{ +#ifdef CONFIG_SND_PCM_XRUN_DEBUG +#define hw_ptr_error(substream, fmt, args...) \ + do { \ + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ + xrun_log_show(substream); \ + pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \ + dump_stack_on_xrun(substream); \ + } \ + } while (0) + +#define XRUN_LOG_CNT 10 + +struct hwptr_log_entry { + unsigned int in_interrupt; + unsigned long jiffies; snd_pcm_uframes_t pos; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t old_hw_ptr; + snd_pcm_uframes_t hw_ptr_base; +}; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - pos = substream->ops->pointer(substream); - if (pos == SNDRV_PCM_POS_XRUN) - return pos; /* XRUN */ -#ifdef CONFIG_SND_DEBUG - if (pos >= runtime->buffer_size) { - snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); +struct snd_pcm_hwptr_log { + unsigned int idx; + unsigned int hit: 1; + struct hwptr_log_entry entries[XRUN_LOG_CNT]; +}; + +static void xrun_log(struct snd_pcm_substream *substream, + snd_pcm_uframes_t pos, int in_interrupt) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hwptr_log *log = runtime->hwptr_log; + struct hwptr_log_entry *entry; + + if (log == NULL) { + log = kzalloc(sizeof(*log), GFP_ATOMIC); + if (log == NULL) + return; + runtime->hwptr_log = log; + } else { + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; } -#endif - pos -= pos % runtime->min_align; - return pos; + entry = &log->entries[log->idx]; + entry->in_interrupt = in_interrupt; + entry->jiffies = jiffies; + entry->pos = pos; + entry->period_size = runtime->period_size; + entry->buffer_size = runtime->buffer_size; + entry->old_hw_ptr = runtime->status->hw_ptr; + entry->hw_ptr_base = runtime->hw_ptr_base; + log->idx = (log->idx + 1) % XRUN_LOG_CNT; } -static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) +static void xrun_log_show(struct snd_pcm_substream *substream) +{ + struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; + struct hwptr_log_entry *entry; + char name[16]; + unsigned int idx; + int cnt; + + if (log == NULL) + return; + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; + snd_pcm_debug_name(substream, name, sizeof(name)); + for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { + entry = &log->entries[idx]; + if (entry->period_size == 0) + break; + pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " + "hwptr=%ld/%ld\n", + name, entry->in_interrupt ? "[Q] " : "", + entry->jiffies, + (unsigned long)entry->pos, + (unsigned long)entry->period_size, + (unsigned long)entry->buffer_size, + (unsigned long)entry->old_hw_ptr, + (unsigned long)entry->hw_ptr_base); + idx++; + idx %= XRUN_LOG_CNT; + } + log->hit = 1; +} + +#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ + +#define hw_ptr_error(substream, fmt, args...) do { } while (0) +#define xrun_log(substream, pos, in_interrupt) do { } while (0) +#define xrun_log_show(substream) do { } while (0) + +#endif + +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t avail; @@ -169,104 +285,277 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream avail = snd_pcm_capture_avail(runtime); if (avail > runtime->avail_max) runtime->avail_max = avail; - if (avail >= runtime->stop_threshold) { - if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + if (avail >= runtime->buffer_size) { snd_pcm_drain_done(substream); - else + return -EPIPE; + } + } else { + if (avail >= runtime->stop_threshold) { xrun(substream); - return -EPIPE; + return -EPIPE; + } } - if (avail >= runtime->control->avail_min) + if (runtime->twake) { + if (avail >= runtime->twake) + wake_up(&runtime->tsleep); + } else if (avail >= runtime->control->avail_min) wake_up(&runtime->sleep); return 0; } -static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) +static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, + unsigned int in_interrupt) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t pos; - snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; - snd_pcm_sframes_t delta; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; + snd_pcm_sframes_t hdelta, delta; + unsigned long jdelta; + unsigned long curr_jiffies; + struct timespec curr_tstamp; + struct timespec audio_tstamp; + int crossed_boundary = 0; + + old_hw_ptr = runtime->status->hw_ptr; + + /* + * group pointer, time and jiffies reads to allow for more + * accurate correlations/corrections. + * The values are stored at the end of this routine after + * corrections for hw_ptr position + */ + pos = substream->ops->pointer(substream); + curr_jiffies = jiffies; + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + + if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && + (substream->ops->wall_clock)) + substream->ops->wall_clock(substream, &audio_tstamp); + } - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; } - if (runtime->period_size == runtime->buffer_size) - goto __next_buf; - new_hw_ptr = runtime->hw_ptr_base + pos; - hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; - - delta = hw_ptr_interrupt - new_hw_ptr; - if (delta > 0) { - if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - if (runtime->periods > 1 && substream->pstr->xrun_debug) { - snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); - if (substream->pstr->xrun_debug > 1) - dump_stack(); + if (pos >= runtime->buffer_size) { + if (printk_ratelimit()) { + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); + xrun_log_show(substream); + pcm_err(substream->pcm, + "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + name, pos, runtime->buffer_size, + runtime->period_size); + } + pos = 0; + } + pos -= pos % runtime->min_align; + if (xrun_debug(substream, XRUN_DEBUG_LOG)) + xrun_log(substream, pos, in_interrupt); + hw_base = runtime->hw_ptr_base; + new_hw_ptr = hw_base + pos; + if (in_interrupt) { + /* we know that one period was processed */ + /* delta = "expected next hw_ptr" for in_interrupt != 0 */ + delta = runtime->hw_ptr_interrupt + runtime->period_size; + if (delta > new_hw_ptr) { + /* check for double acknowledged interrupts */ + hdelta = curr_jiffies - runtime->hw_ptr_jiffies; + if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) { + hw_base = 0; + crossed_boundary++; + } + new_hw_ptr = hw_base + pos; + goto __delta; } -#endif - return 0; } - __next_buf: - runtime->hw_ptr_base += runtime->buffer_size; - if (runtime->hw_ptr_base == runtime->boundary) - runtime->hw_ptr_base = 0; - new_hw_ptr = runtime->hw_ptr_base + pos; + } + /* new_hw_ptr might be lower than old_hw_ptr in case when */ + /* pointer crosses the end of the ring buffer */ + if (new_hw_ptr < old_hw_ptr) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) { + hw_base = 0; + crossed_boundary++; + } + new_hw_ptr = hw_base + pos; + } + __delta: + delta = new_hw_ptr - old_hw_ptr; + if (delta < 0) + delta += runtime->boundary; + if (xrun_debug(substream, in_interrupt ? + XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); + pcm_dbg(substream->pcm, + "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n", + in_interrupt ? "period" : "hwptr", + name, + (unsigned int)pos, + (unsigned int)runtime->period_size, + (unsigned int)runtime->buffer_size, + (unsigned long)delta, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr, + (unsigned long)runtime->hw_ptr_base); + } + + if (runtime->no_period_wakeup) { + snd_pcm_sframes_t xrun_threshold; + /* + * Without regular period interrupts, we have to check + * the elapsed time to detect xruns. + */ + jdelta = curr_jiffies - runtime->hw_ptr_jiffies; + if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) + goto no_delta_check; + hdelta = jdelta - delta * HZ / runtime->rate; + xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; + while (hdelta > xrun_threshold) { + delta += runtime->buffer_size; + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) { + hw_base = 0; + crossed_boundary++; + } + new_hw_ptr = hw_base + pos; + hdelta -= runtime->hw_ptr_buffer_jiffies; + } + goto no_delta_check; + } + + /* something must be really wrong */ + if (delta >= runtime->buffer_size + runtime->period_size) { + hw_ptr_error(substream, + "Unexpected hw_pointer value %s" + "(stream=%i, pos=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "[P]", + substream->stream, (long)pos, + (long)new_hw_ptr, (long)old_hw_ptr); + return 0; } + /* Do jiffies check only in xrun_debug mode */ + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) + goto no_jiffies_check; + + /* Skip the jiffies check for hardwares with BATCH flag. + * Such hardware usually just increases the position at each IRQ, + * thus it can't give any strange position. + */ + if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) + goto no_jiffies_check; + hdelta = delta; + if (hdelta < runtime->delay) + goto no_jiffies_check; + hdelta -= runtime->delay; + jdelta = curr_jiffies - runtime->hw_ptr_jiffies; + if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { + delta = jdelta / + (((runtime->period_size * HZ) / runtime->rate) + + HZ/100); + /* move new_hw_ptr according jiffies not pos variable */ + new_hw_ptr = old_hw_ptr; + hw_base = delta; + /* use loop to avoid checks for delta overflows */ + /* the delta value is small or zero in most cases */ + while (delta > 0) { + new_hw_ptr += runtime->period_size; + if (new_hw_ptr >= runtime->boundary) { + new_hw_ptr -= runtime->boundary; + crossed_boundary--; + } + delta--; + } + /* align hw_base to buffer_size */ + hw_ptr_error(substream, + "hw_ptr skipping! %s" + "(pos=%ld, delta=%ld, period=%ld, " + "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", + in_interrupt ? "[Q] " : "", + (long)pos, (long)hdelta, + (long)runtime->period_size, jdelta, + ((hdelta * HZ) / runtime->rate), hw_base, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr); + /* reset values to proper state */ + delta = 0; + hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); + } + no_jiffies_check: + if (delta > runtime->period_size + runtime->period_size / 2) { + hw_ptr_error(substream, + "Lost interrupts? %s" + "(stream=%i, delta=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "", + substream->stream, (long)delta, + (long)new_hw_ptr, + (long)old_hw_ptr); + } + + no_delta_check: + if (runtime->status->hw_ptr == new_hw_ptr) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, new_hw_ptr); + if (in_interrupt) { + delta = new_hw_ptr - runtime->hw_ptr_interrupt; + if (delta < 0) + delta += runtime->boundary; + delta -= (snd_pcm_uframes_t)delta % runtime->period_size; + runtime->hw_ptr_interrupt += delta; + if (runtime->hw_ptr_interrupt >= runtime->boundary) + runtime->hw_ptr_interrupt -= runtime->boundary; + } + runtime->hw_ptr_base = hw_base; runtime->status->hw_ptr = new_hw_ptr; - runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; + runtime->hw_ptr_jiffies = curr_jiffies; + if (crossed_boundary) { + snd_BUG_ON(crossed_boundary != 1); + runtime->hw_ptr_wrap += runtime->boundary; + } + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { + runtime->status->tstamp = curr_tstamp; + + if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { + /* + * no wall clock available, provide audio timestamp + * derived from pointer position+delay + */ + u64 audio_frames, audio_nsecs; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames = runtime->hw_ptr_wrap + + runtime->status->hw_ptr + - runtime->delay; + else + audio_frames = runtime->hw_ptr_wrap + + runtime->status->hw_ptr + + runtime->delay; + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = audio_tstamp; + } - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_state(substream, runtime); } /* CAUTION: call it with irq disabled */ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t pos; - snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; - snd_pcm_sframes_t delta; - - old_hw_ptr = runtime->status->hw_ptr; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); - if (pos == SNDRV_PCM_POS_XRUN) { - xrun(substream); - return -EPIPE; - } - new_hw_ptr = runtime->hw_ptr_base + pos; - - delta = old_hw_ptr - new_hw_ptr; - if (delta > 0) { - if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - if (runtime->periods > 2 && substream->pstr->xrun_debug) { - snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); - if (substream->pstr->xrun_debug > 1) - dump_stack(); - } -#endif - return 0; - } - runtime->hw_ptr_base += runtime->buffer_size; - if (runtime->hw_ptr_base == runtime->boundary) - runtime->hw_ptr_base = 0; - new_hw_ptr = runtime->hw_ptr_base + pos; - } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, new_hw_ptr); - - runtime->status->hw_ptr = new_hw_ptr; - - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_hw_ptr0(substream, 0); } /** @@ -277,7 +566,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) * * Sets the given PCM operators to the pcm instance. */ -void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) +void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, + const struct snd_pcm_ops *ops) { struct snd_pcm_str *stream = &pcm->streams[direction]; struct snd_pcm_substream *substream; @@ -354,11 +644,11 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, { u_int64_t n = (u_int64_t) a * b; if (c == 0) { - snd_assert(n > 0, ); + snd_BUG_ON(!n); *r = 0; return UINT_MAX; } - div64_32(&n, c, r); + n = div_u64_rem(n, c, r); if (n >= UINT_MAX) { *r = 0; return UINT_MAX; @@ -375,12 +665,14 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, * The interval is changed to the range satisfying both intervals. * The interval status (min, max, integer, etc.) are evaluated. * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) { int changed = 0; - snd_assert(!snd_interval_empty(i), return -EINVAL); + if (snd_BUG_ON(snd_interval_empty(i))) + return -EINVAL; if (i->min < v->min) { i->min = v->min; i->openmin = v->openmin; @@ -423,7 +715,8 @@ EXPORT_SYMBOL(snd_interval_refine); static int snd_interval_refine_first(struct snd_interval *i) { - snd_assert(!snd_interval_empty(i), return -EINVAL); + if (snd_BUG_ON(snd_interval_empty(i))) + return -EINVAL; if (snd_interval_single(i)) return 0; i->max = i->min; @@ -435,7 +728,8 @@ static int snd_interval_refine_first(struct snd_interval *i) static int snd_interval_refine_last(struct snd_interval *i) { - snd_assert(!snd_interval_empty(i), return -EINVAL); + if (snd_BUG_ON(snd_interval_empty(i))) + return -EINVAL; if (snd_interval_single(i)) return 0; i->min = i->max; @@ -571,16 +865,20 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp) { - unsigned int best_num, best_diff, best_den; + unsigned int best_num, best_den; + int best_diff; unsigned int k; struct snd_interval t; int err; + unsigned int result_num, result_den; + int result_diff; best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { @@ -590,7 +888,7 @@ int snd_interval_ratnum(struct snd_interval *i, int diff; if (q == 0) q = 1; - den = div_down(num, q); + den = div_up(num, q); if (den < rats[k].den_min) continue; if (den > rats[k].den_max) @@ -602,6 +900,8 @@ int snd_interval_ratnum(struct snd_interval *i, den -= r; } diff = num - q * den; + if (diff < 0) + diff = -diff; if (best_num == 0 || diff * best_den < best_diff * den) { best_diff = diff; @@ -616,6 +916,9 @@ int snd_interval_ratnum(struct snd_interval *i, t.min = div_down(best_num, best_den); t.openmin = !!(best_num % best_den); + result_num = best_num; + result_diff = best_diff; + result_den = best_den; best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsigned int num = rats[k].num; @@ -626,7 +929,7 @@ int snd_interval_ratnum(struct snd_interval *i, i->empty = 1; return -EINVAL; } - den = div_up(num, q); + den = div_down(num, q); if (den > rats[k].den_max) continue; if (den < rats[k].den_min) @@ -638,6 +941,8 @@ int snd_interval_ratnum(struct snd_interval *i, den += rats[k].den_step - r; } diff = q * den - num; + if (diff < 0) + diff = -diff; if (best_num == 0 || diff * best_den < best_diff * den) { best_diff = diff; @@ -657,10 +962,14 @@ int snd_interval_ratnum(struct snd_interval *i, return err; if (snd_interval_single(i)) { + if (best_diff * result_den < result_diff * best_den) { + result_num = best_num; + result_den = best_den; + } if (nump) - *nump = best_num; + *nump = result_num; if (denp) - *denp = best_den; + *denp = result_den; } return err; } @@ -675,7 +984,8 @@ EXPORT_SYMBOL(snd_interval_ratnum); * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ static int snd_interval_ratden(struct snd_interval *i, unsigned int rats_count, struct snd_ratden *rats, @@ -774,52 +1084,31 @@ static int snd_interval_ratden(struct snd_interval *i, * When mask is non-zero, only the elements corresponding to bit 1 are * evaluated. * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ -int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) +int snd_interval_list(struct snd_interval *i, unsigned int count, + const unsigned int *list, unsigned int mask) { unsigned int k; - int changed = 0; + struct snd_interval list_range; if (!count) { i->empty = 1; return -EINVAL; } + snd_interval_any(&list_range); + list_range.min = UINT_MAX; + list_range.max = 0; for (k = 0; k < count; k++) { if (mask && !(mask & (1 << k))) continue; - if (i->min == list[k] && !i->openmin) - goto _l1; - if (i->min < list[k]) { - i->min = list[k]; - i->openmin = 0; - changed = 1; - goto _l1; - } - } - i->empty = 1; - return -EINVAL; - _l1: - for (k = count; k-- > 0;) { - if (mask && !(mask & (1 << k))) + if (!snd_interval_test(i, list[k])) continue; - if (i->max == list[k] && !i->openmax) - goto _l2; - if (i->max > list[k]) { - i->max = list[k]; - i->openmax = 0; - changed = 1; - goto _l2; - } + list_range.min = min(list_range.min, list[k]); + list_range.max = max(list_range.max, list[k]); } - i->empty = 1; - return -EINVAL; - _l2: - if (snd_interval_checkempty(i)) { - i->empty = 1; - return -EINVAL; - } - return changed; + return snd_interval_refine(i, &list_range); } EXPORT_SYMBOL(snd_interval_list); @@ -856,7 +1145,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned * @private: the private data pointer passed to function * @dep: the dependent variables * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, int var, @@ -872,8 +1161,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, struct snd_pcm_hw_rule *new; unsigned int new_rules = constrs->rules_all + 16; new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); - if (!new) + if (!new) { + va_end(args); return -ENOMEM; + } if (constrs->rules) { memcpy(new, constrs->rules, constrs->rules_num * sizeof(*c)); @@ -889,7 +1180,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, c->private = private; k = 0; while (1) { - snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); + if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { + va_end(args); + return -EINVAL; + } c->deps[k++] = dep; if (dep < 0) break; @@ -898,17 +1192,19 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, constrs->rules_num++; va_end(args); return 0; -} +} EXPORT_SYMBOL(snd_pcm_hw_rule_add); /** - * snd_pcm_hw_constraint_mask + * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the bitmap mask * - * Apply the constraint of the given bitmap mask to a mask parameter. + * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int32_t mask) @@ -923,12 +1219,14 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param } /** - * snd_pcm_hw_constraint_mask64 + * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the 64bit bitmap mask * - * Apply the constraint of the given bitmap mask to a mask parameter. + * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int64_t mask) @@ -942,13 +1240,17 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par return -EINVAL; return 0; } +EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64); /** - * snd_pcm_hw_constraint_integer + * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the integer constraint * * Apply the constraint of integer to an interval parameter. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) { @@ -959,13 +1261,16 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); /** - * snd_pcm_hw_constraint_minmax + * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the range * @min: the minimal value * @max: the maximal value * * Apply the min/max range constraint to an interval parameter. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, unsigned int min, unsigned int max) @@ -990,21 +1295,23 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_constraint_list + * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the list constraint * @l: list * * Apply the list of constraints to an interval parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_list *l) + const struct snd_pcm_hw_constraint_list *l) { return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_list, l, + snd_pcm_hw_rule_list, (void *)l, var, -1); } @@ -1026,11 +1333,13 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_ratnums + * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the ratnums constraint * @r: struct snd_ratnums constriants + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1059,11 +1368,13 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_ratdens + * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the ratdens constraint * @r: struct snd_ratdens constriants + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1090,11 +1401,13 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_msbits + * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule * @runtime: PCM runtime instance * @cond: condition bits * @width: sample bits width * @msbits: msbits width + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1118,11 +1431,13 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_step + * snd_pcm_hw_constraint_step - add a hw constraint step rule * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the step constraint * @step: step size + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1149,10 +1464,12 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm } /** - * snd_pcm_hw_constraint_pow2 + * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the power-of-2 constraint + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1165,6 +1482,34 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); +static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; + struct snd_interval *rate; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + return snd_interval_list(rate, 1, &base_rate, 0); +} + +/** + * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling + * @runtime: PCM runtime instance + * @base_rate: the rate at which the hardware does not resample + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, + unsigned int base_rate) +{ + return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, + SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_noresample_func, + (void *)(uintptr_t)base_rate, + SNDRV_PCM_HW_PARAM_RATE, -1); +} +EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); + static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { @@ -1197,13 +1542,13 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** - * snd_pcm_hw_param_value + * snd_pcm_hw_param_value - return @params field @var value * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Return the value for field PAR if it's fixed in configuration space - * defined by PARAMS. Return -EINVAL otherwise + * Return: The value for field @var if it's fixed in configuration space + * defined by @params. -%EINVAL otherwise. */ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir) @@ -1266,15 +1611,16 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_param_first + * snd_pcm_hw_param_first - refine config space and return minimum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Inside configuration space defined by PARAMS remove from PAR all + * Inside configuration space defined by @params remove from @var all * values > minimum. Reduce configuration space accordingly. - * Return the minimum. + * + * Return: The minimum, or a negative error code on failure. */ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, @@ -1285,7 +1631,8 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - snd_assert(err >= 0, return err); + if (snd_BUG_ON(err < 0)) + return err; } return snd_pcm_hw_param_value(params, var, dir); } @@ -1311,15 +1658,16 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_param_last + * snd_pcm_hw_param_last - refine config space and return maximum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Inside configuration space defined by PARAMS remove from PAR all + * Inside configuration space defined by @params remove from @var all * values < maximum. Reduce configuration space accordingly. - * Return the maximum. + * + * Return: The maximum, or a negative error code on failure. */ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, @@ -1330,7 +1678,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - snd_assert(err >= 0, return err); + if (snd_BUG_ON(err < 0)) + return err; } return snd_pcm_hw_param_value(params, var, dir); } @@ -1338,14 +1687,16 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, EXPORT_SYMBOL(snd_pcm_hw_param_last); /** - * snd_pcm_hw_param_choose + * snd_pcm_hw_param_choose - choose a configuration defined by @params * @pcm: PCM instance * @params: the hw_params instance * - * Choose one configuration from configuration space defined by PARAMS + * Choose one configuration from configuration space defined by @params. * The configuration chosen is that obtained fixing in this order: * first access, first format, first subformat, min channels, * min rate, min period time, max buffer size, min tick time + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params) @@ -1368,7 +1719,8 @@ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, err = snd_pcm_hw_param_first(pcm, params, *v, NULL); else err = snd_pcm_hw_param_last(pcm, params, *v, NULL); - snd_assert(err >= 0, return err); + if (snd_BUG_ON(err < 0)) + return err; } return 0; } @@ -1382,8 +1734,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, if (snd_pcm_running(substream) && snd_pcm_update_hw_ptr(substream) >= 0) runtime->status->hw_ptr %= runtime->buffer_size; - else + else { runtime->status->hw_ptr = 0; + runtime->hw_ptr_wrap = 0; + } snd_pcm_stream_unlock_irqrestore(substream, flags); return 0; } @@ -1423,6 +1777,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, return 0; } +static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, + void *arg) +{ + struct snd_pcm_hw_params *params = arg; + snd_pcm_format_t format; + int channels, width; + + params->fifo_size = substream->runtime->hw.fifo_size; + if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { + format = params_format(params); + channels = params_channels(params); + width = snd_pcm_format_physical_width(format); + params->fifo_size /= width * channels; + } + return 0; +} + /** * snd_pcm_lib_ioctl - a generic PCM ioctl callback * @substream: the pcm substream instance @@ -1432,7 +1803,7 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, * Processes the generic ioctl commands for PCM. * Can be passed as the ioctl callback for PCM ops. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) @@ -1444,6 +1815,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: return snd_pcm_lib_ioctl_channel_info(substream, arg); + case SNDRV_PCM_IOCTL1_FIFO_SIZE: + return snd_pcm_lib_ioctl_fifo_size(substream, arg); } return -ENXIO; } @@ -1466,16 +1839,16 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime; unsigned long flags; - snd_assert(substream != NULL, return); + if (PCM_RUNTIME_CHECK(substream)) + return; runtime = substream->runtime; - snd_assert(runtime != NULL, return); if (runtime->transfer_ack_begin) runtime->transfer_ack_begin(substream); snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || - snd_pcm_update_hw_ptr_interrupt(substream) < 0) + snd_pcm_update_hw_ptr0(substream, 1) < 0) goto _end; if (substream->timer_running) @@ -1495,7 +1868,7 @@ EXPORT_SYMBOL(snd_pcm_period_elapsed); * The available space is stored on availp. When err = 0 and avail = 0 * on the capture stream, it indicates the stream is in DRAINING state. */ -static int wait_for_avail_min(struct snd_pcm_substream *substream, +static int wait_for_avail(struct snd_pcm_substream *substream, snd_pcm_uframes_t *availp) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -1503,19 +1876,48 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, wait_queue_t wait; int err = 0; snd_pcm_uframes_t avail = 0; - long tout; + long wait_time, tout; init_waitqueue_entry(&wait, current); - add_wait_queue(&runtime->sleep, &wait); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&runtime->tsleep, &wait); + + if (runtime->no_period_wakeup) + wait_time = MAX_SCHEDULE_TIMEOUT; + else { + wait_time = 10; + if (runtime->rate) { + long t = runtime->period_size * 2 / runtime->rate; + wait_time = max(t, wait_time); + } + wait_time = msecs_to_jiffies(wait_time * 1000); + } + for (;;) { if (signal_pending(current)) { err = -ERESTARTSYS; break; } - set_current_state(TASK_INTERRUPTIBLE); + + /* + * We need to check if space became available already + * (and thus the wakeup happened already) first to close + * the race of space already having become available. + * This check must happen after been added to the waitqueue + * and having current state be INTERRUPTIBLE. + */ + if (is_playback) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail >= runtime->twake) + break; snd_pcm_stream_unlock_irq(substream); - tout = schedule_timeout(msecs_to_jiffies(10000)); + + tout = schedule_timeout(wait_time); + snd_pcm_stream_lock_irq(substream); + set_current_state(TASK_INTERRUPTIBLE); switch (runtime->status->state) { case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; @@ -1534,22 +1936,20 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, case SNDRV_PCM_STATE_DISCONNECTED: err = -EBADFD; goto _endloop; + case SNDRV_PCM_STATE_PAUSED: + continue; } if (!tout) { - snd_printd("%s write error (DMA or IRQ trouble?)\n", - is_playback ? "playback" : "capture"); + pcm_dbg(substream->pcm, + "%s write error (DMA or IRQ trouble?)\n", + is_playback ? "playback" : "capture"); err = -EIO; break; } - if (is_playback) - avail = snd_pcm_playback_avail(runtime); - else - avail = snd_pcm_capture_avail(runtime); - if (avail >= runtime->control->avail_min) - break; } _endloop: - remove_wait_queue(&runtime->sleep, &wait); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->tsleep, &wait); *availp = avail; return err; } @@ -1567,7 +1967,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, return err; } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - snd_assert(runtime->dma_area, return -EFAULT); if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) return -EFAULT; } @@ -1587,6 +1986,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; + snd_pcm_uframes_t avail; int err = 0; if (size == 0) @@ -1609,19 +2009,21 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, goto _end_unlock; } + runtime->twake = runtime->control->avail_min ? : 1; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_playback_avail(runtime); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; - snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_playback_avail(runtime); if (!avail) { if (nonblock) { err = -EAGAIN; goto _end_unlock; } - err = wait_for_avail_min(substream, &avail); + runtime->twake = min_t(snd_pcm_uframes_t, size, + runtime->control->avail_min ? : 1); + err = wait_for_avail(substream, &avail); if (err < 0) goto _end_unlock; } @@ -1629,13 +2031,18 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; - snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); + if (snd_BUG_ON(!frames)) { + runtime->twake = 0; + snd_pcm_stream_unlock_irq(substream); + return -EINVAL; + } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -1656,6 +2063,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, offset += frames; size -= frames; xfer += frames; + avail -= frames; if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { err = snd_pcm_start(substream); @@ -1664,23 +2072,37 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, } } _end_unlock: + runtime->twake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +/* sanity-check for read/write methods */ +static int pcm_sanity_check(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - int nonblock; - - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); - snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) + return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; + return 0; +} +snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +{ + struct snd_pcm_runtime *runtime; + int nonblock; + int err; + + err = pcm_sanity_check(substream); + if (err < 0) + return err; + runtime = substream->runtime; nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && @@ -1703,7 +2125,8 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, int channels = runtime->channels; int c; if (substream->ops->copy) { - snd_assert(substream->ops->silence != NULL, return -EINVAL); + if (snd_BUG_ON(!substream->ops->silence)) + return -EINVAL; for (c = 0; c < channels; ++c, ++bufs) { if (*bufs == NULL) { if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) @@ -1717,7 +2140,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, } else { /* default transfer behaviour */ size_t dma_csize = runtime->dma_bytes / channels; - snd_assert(runtime->dma_area, return -EFAULT); for (c = 0; c < channels; ++c, ++bufs) { char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); if (*bufs == NULL) { @@ -1738,14 +2160,12 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime; int nonblock; + int err; - snd_assert(substream != NULL, return -ENXIO); + err = pcm_sanity_check(substream); + if (err < 0) + return err; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); - snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) @@ -1769,7 +2189,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, return err; } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - snd_assert(runtime->dma_area, return -EFAULT); if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) return -EFAULT; } @@ -1785,6 +2204,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; + snd_pcm_uframes_t avail; int err = 0; if (size == 0) @@ -1814,13 +2234,13 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, goto _end_unlock; } + runtime->twake = runtime->control->avail_min ? : 1; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_capture_avail(runtime); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; - snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_capture_avail(runtime); if (!avail) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1831,7 +2251,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, err = -EAGAIN; goto _end_unlock; } - err = wait_for_avail_min(substream, &avail); + runtime->twake = min_t(snd_pcm_uframes_t, size, + runtime->control->avail_min ? : 1); + err = wait_for_avail(substream, &avail); if (err < 0) goto _end_unlock; if (!avail) @@ -1841,13 +2263,18 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; - snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); + if (snd_BUG_ON(!frames)) { + runtime->twake = 0; + snd_pcm_stream_unlock_irq(substream); + return -EINVAL; + } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -1868,10 +2295,13 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, offset += frames; size -= frames; xfer += frames; + avail -= frames; } _end_unlock: + runtime->twake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } @@ -1879,14 +2309,12 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u { struct snd_pcm_runtime *runtime; int nonblock; + int err; - snd_assert(substream != NULL, return -ENXIO); + err = pcm_sanity_check(substream); + if (err < 0) + return err; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); - snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) return -EINVAL; @@ -1916,7 +2344,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, } } else { snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; - snd_assert(runtime->dma_area, return -EFAULT); for (c = 0; c < channels; ++c, ++bufs) { char *hwbuf; char __user *buf; @@ -1938,11 +2365,12 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime; int nonblock; + int err; - snd_assert(substream != NULL, return -ENXIO); + err = pcm_sanity_check(substream); + if (err < 0) + return err; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); - snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -1953,3 +2381,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). + * Return: Zero if successful, 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 ff07b4a9992..54debc07f5c 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -22,7 +22,10 @@ #include <asm/io.h> #include <linux/time.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/moduleparam.h> +#include <linux/vmalloc.h> +#include <linux/export.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/info.h> @@ -48,19 +51,9 @@ static const size_t snd_minimum_buffer = 16384; static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size) { struct snd_dma_buffer *dmab = &substream->dma_buffer; + size_t orig_size = size; int err; - snd_assert(size > 0, return -EINVAL); - - /* already reserved? */ - if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) { - if (dmab->bytes >= size) - return 0; /* yes */ - /* no, free the reserved block */ - snd_dma_free_pages(dmab); - dmab->bytes = 0; - } - do { if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab)) < 0) { @@ -71,6 +64,10 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz size >>= 1; } while (size >= snd_minimum_buffer); dmab->bytes = 0; /* tell error */ + pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", + substream->pcm->card->number, substream->pcm->device, + substream->stream ? 'c' : 'p', substream->number, + substream->pcm->name, orig_size); return 0; } @@ -81,10 +78,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream { if (substream->dma_buffer.area == NULL) return; - if (substream->dma_buf_id) - snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id); - else - snd_dma_free_pages(&substream->dma_buffer); + snd_dma_free_pages(&substream->dma_buffer); substream->dma_buffer.area = NULL; } @@ -94,7 +88,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream * * Releases the pre-allocated buffer of the given substream. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) { @@ -114,7 +108,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) * * Releases all the pre-allocated buffers on the given pcm. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) { @@ -253,18 +247,13 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream, * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type * @substream: the pcm substream instance * @type: DMA type (SNDRV_DMA_TYPE_*) - * @data: DMA type dependant data + * @data: DMA type dependent data * @size: the requested pre-allocation size in bytes * @max: the max. allowed pre-allocation size * * Do pre-allocation for the given DMA buffer type. * - * When substream->dma_buf_id is set, the function tries to look for - * the reserved buffer, and the buffer is not freed but reserved at - * destruction time. The dma_buf_id must be unique for all systems - * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id(). - * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, int type, struct device *data, @@ -278,17 +267,17 @@ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); /** - * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) + * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams) * @pcm: the pcm instance * @type: DMA type (SNDRV_DMA_TYPE_*) - * @data: DMA type dependant data + * @data: DMA type dependent data * @size: the requested pre-allocation size in bytes * @max: the max. allowed pre-allocation size * * Do pre-allocation to all substreams of the given pcm for the * specified DMA type. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, int type, void *data, @@ -306,13 +295,15 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +#ifdef CONFIG_SND_DMA_SGBUF /** * snd_pcm_sgbuf_ops_page - get the page struct at the given offset * @substream: the pcm substream instance * @offset: the buffer offset * - * Returns the page struct at the given buffer offset. * Used as the page callback of PCM ops. + * + * Return: The page struct at the given buffer offset. %NULL on failure. */ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset) { @@ -325,6 +316,7 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne } EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); +#endif /* CONFIG_SND_DMA_SGBUF */ /** * snd_pcm_lib_malloc_pages - allocate the DMA buffer @@ -334,7 +326,7 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); * Allocates the DMA buffer on the BUS type given earlier to * snd_pcm_lib_preallocate_xxx_pages(). * - * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * Return: 1 if the buffer is changed, 0 if not changed, or a negative * code on failure. */ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) @@ -342,10 +334,12 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) struct snd_pcm_runtime *runtime; struct snd_dma_buffer *dmab = NULL; - snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); - snd_assert(substream != NULL, return -EINVAL); + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; + if (snd_BUG_ON(substream->dma_buffer.dev.type == + SNDRV_DMA_TYPE_UNKNOWN)) + return -EINVAL; runtime = substream->runtime; - snd_assert(runtime != NULL, return -EINVAL); if (runtime->dma_buffer_p) { /* perphaps, we might free the large DMA memory region @@ -385,15 +379,15 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); * * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - snd_assert(substream != NULL, return -EINVAL); + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; runtime = substream->runtime; - snd_assert(runtime != NULL, return -EINVAL); if (runtime->dma_area == NULL) return 0; if (runtime->dma_buffer_p != &substream->dma_buffer) { @@ -406,3 +400,61 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_pcm_lib_free_pages); + +int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, + size_t size, gfp_t gfp_flags) +{ + struct snd_pcm_runtime *runtime; + + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; + runtime = substream->runtime; + if (runtime->dma_area) { + if (runtime->dma_bytes >= size) + return 0; /* already large enough */ + vfree(runtime->dma_area); + } + runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL); + if (!runtime->dma_area) + return -ENOMEM; + runtime->dma_bytes = size; + return 1; +} +EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer); + +/** + * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer + * @substream: the substream with a buffer allocated by + * snd_pcm_lib_alloc_vmalloc_buffer() + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; + runtime = substream->runtime; + vfree(runtime->dma_area); + runtime->dma_area = NULL; + return 0; +} +EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer); + +/** + * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct + * @substream: the substream with a buffer allocated by + * snd_pcm_lib_alloc_vmalloc_buffer() + * @offset: offset in the buffer + * + * This function is to be used as the page callback in the PCM ops. + * + * Return: The page struct, or %NULL on failure. + */ +struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + return vmalloc_to_page(substream->runtime->dma_area + offset); +} +EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page); diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 89b7f549beb..4560ca0e565 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -20,6 +20,7 @@ */ #include <linux/time.h> +#include <linux/export.h> #include <sound/core.h> #include <sound/pcm.h> #define SND_PCM_FORMAT_UNKNOWN (-1) @@ -35,7 +36,10 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { +/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ +#define INT __force int + +static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -128,6 +132,22 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { .width = 4, .phys = 4, .le = -1, .signd = -1, .silence = {}, }, + [SNDRV_PCM_FORMAT_G723_24] = { + .width = 3, .phys = 3, .le = -1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_G723_40] = { + .width = 5, .phys = 5, .le = -1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_DSD_U8] = { + .width = 8, .phys = 8, .le = 1, .signd = 0, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_DSD_U16_LE] = { + .width = 16, .phys = 16, .le = 1, .signd = 0, + .silence = {}, + }, /* FIXME: the following three formats are not defined properly yet */ [SNDRV_PCM_FORMAT_MPEG] = { .le = -1, .signd = -1, @@ -186,6 +206,14 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { .width = 18, .phys = 24, .le = 0, .signd = 0, .silence = { 0x02, 0x00, 0x00 }, }, + [SNDRV_PCM_FORMAT_G723_24_1B] = { + .width = 3, .phys = 8, .le = -1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_G723_40_1B] = { + .width = 5, .phys = 8, .le = -1, .signd = -1, + .silence = {}, + }, }; @@ -193,15 +221,15 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { * snd_pcm_format_signed - Check the PCM format is signed linear * @format: the format to check * - * Returns 1 if the given PCM format is signed linear, 0 if unsigned + * Return: 1 if the given PCM format is signed linear, 0 if unsigned * linear, and a negative error code for non-linear formats. */ int snd_pcm_format_signed(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].signd) < 0) + if ((val = pcm_formats[(INT)format].signd) < 0) return -EINVAL; return val; } @@ -212,7 +240,7 @@ EXPORT_SYMBOL(snd_pcm_format_signed); * snd_pcm_format_unsigned - Check the PCM format is unsigned linear * @format: the format to check * - * Returns 1 if the given PCM format is unsigned linear, 0 if signed + * Return: 1 if the given PCM format is unsigned linear, 0 if signed * linear, and a negative error code for non-linear formats. */ int snd_pcm_format_unsigned(snd_pcm_format_t format) @@ -231,7 +259,7 @@ EXPORT_SYMBOL(snd_pcm_format_unsigned); * snd_pcm_format_linear - Check the PCM format is linear * @format: the format to check * - * Returns 1 if the given PCM format is linear, 0 if not. + * Return: 1 if the given PCM format is linear, 0 if not. */ int snd_pcm_format_linear(snd_pcm_format_t format) { @@ -244,15 +272,15 @@ EXPORT_SYMBOL(snd_pcm_format_linear); * snd_pcm_format_little_endian - Check the PCM format is little-endian * @format: the format to check * - * Returns 1 if the given PCM format is little-endian, 0 if + * Return: 1 if the given PCM format is little-endian, 0 if * big-endian, or a negative error code if endian not specified. */ int snd_pcm_format_little_endian(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].le) < 0) + if ((val = pcm_formats[(INT)format].le) < 0) return -EINVAL; return val; } @@ -263,7 +291,7 @@ EXPORT_SYMBOL(snd_pcm_format_little_endian); * snd_pcm_format_big_endian - Check the PCM format is big-endian * @format: the format to check * - * Returns 1 if the given PCM format is big-endian, 0 if + * Return: 1 if the given PCM format is big-endian, 0 if * little-endian, or a negative error code if endian not specified. */ int snd_pcm_format_big_endian(snd_pcm_format_t format) @@ -282,15 +310,15 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); * snd_pcm_format_width - return the bit-width of the format * @format: the format to check * - * Returns the bit-width of the format, or a negative error code + * Return: The bit-width of the format, or a negative error code * if unknown format. */ int snd_pcm_format_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].width) == 0) + if ((val = pcm_formats[(INT)format].width) == 0) return -EINVAL; return val; } @@ -301,15 +329,15 @@ EXPORT_SYMBOL(snd_pcm_format_width); * snd_pcm_format_physical_width - return the physical bit-width of the format * @format: the format to check * - * Returns the physical bit-width of the format, or a negative error code + * Return: The physical bit-width of the format, or a negative error code * if unknown format. */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].phys) == 0) + if ((val = pcm_formats[(INT)format].phys) == 0) return -EINVAL; return val; } @@ -319,8 +347,9 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width); /** * snd_pcm_format_size - return the byte size of samples on the given format * @format: the format to check + * @samples: sampling rate * - * Returns the byte size of the given samples for the format, or a + * Return: The byte size of the given samples for the format, or a * negative error code if unknown format. */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) @@ -337,15 +366,15 @@ EXPORT_SYMBOL(snd_pcm_format_size); * snd_pcm_format_silence_64 - return the silent data in 8 bytes array * @format: the format to check * - * Returns the format pattern to fill or NULL if error. + * Return: The format pattern to fill or %NULL if error. */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return NULL; - if (! pcm_formats[format].phys) + if (! pcm_formats[(INT)format].phys) return NULL; - return pcm_formats[format].silence; + return pcm_formats[(INT)format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -358,23 +387,23 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64); * * Sets the silence data on the buffer for the given samples. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) { int width; unsigned char *dst, *pat; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[format].phys; /* physical width */ - pat = pcm_formats[format].silence; + width = pcm_formats[(INT)format].phys; /* physical width */ + pat = pcm_formats[(INT)format].silence; if (! width) return -EINVAL; /* signed or 1 byte data */ - if (pcm_formats[format].signd == 1 || width <= 8) { + if (pcm_formats[(INT)format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; @@ -428,7 +457,7 @@ EXPORT_SYMBOL(snd_pcm_format_set_silence); * Determines the rate_min and rate_max fields from the rates bits of * the given runtime->hw. * - * Returns zero if successful. + * Return: Zero if successful. */ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) { @@ -454,7 +483,7 @@ EXPORT_SYMBOL(snd_pcm_limit_hw_rates); * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit * @rate: the sample rate to convert * - * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or + * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or * SNDRV_PCM_RATE_KNOT for an unknown rate. */ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) @@ -467,3 +496,60 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) return SNDRV_PCM_RATE_KNOT; } EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit); + +/** + * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate + * @rate_bit: the rate bit to convert + * + * Return: The sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag + * or 0 for an unknown rate bit. + */ +unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit) +{ + unsigned int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) + if ((1u << i) == rate_bit) + return snd_pcm_known_rates.list[i]; + return 0; +} +EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate); + +static unsigned int snd_pcm_rate_mask_sanitize(unsigned int rates) +{ + if (rates & SNDRV_PCM_RATE_CONTINUOUS) + return SNDRV_PCM_RATE_CONTINUOUS; + else if (rates & SNDRV_PCM_RATE_KNOT) + return SNDRV_PCM_RATE_KNOT; + return rates; +} + +/** + * snd_pcm_rate_mask_intersect - computes the intersection between two rate masks + * @rates_a: The first rate mask + * @rates_b: The second rate mask + * + * This function computes the rates that are supported by both rate masks passed + * to the function. It will take care of the special handling of + * SNDRV_PCM_RATE_CONTINUOUS and SNDRV_PCM_RATE_KNOT. + * + * Return: A rate mask containing the rates that are supported by both rates_a + * and rates_b. + */ +unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, + unsigned int rates_b) +{ + rates_a = snd_pcm_rate_mask_sanitize(rates_a); + rates_b = snd_pcm_rate_mask_sanitize(rates_b); + + if (rates_a & SNDRV_PCM_RATE_CONTINUOUS) + return rates_b; + else if (rates_b & SNDRV_PCM_RATE_CONTINUOUS) + return rates_a; + else if (rates_a & SNDRV_PCM_RATE_KNOT) + return rates_b; + else if (rates_b & SNDRV_PCM_RATE_KNOT) + return rates_a; + return rates_a & rates_b; +} +EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 61f5d425b63..b653ab001fb 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -20,11 +20,13 @@ */ #include <linux/mm.h> +#include <linux/module.h> #include <linux/file.h> #include <linux/slab.h> #include <linux/time.h> -#include <linux/pm_qos_params.h> -#include <linux/uio.h> +#include <linux/pm_qos.h> +#include <linux/aio.h> +#include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/control.h> #include <sound/info.h> @@ -33,6 +35,9 @@ #include <sound/timer.h> #include <sound/minors.h> #include <asm/io.h> +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) +#include <dma-coherence.h> +#endif /* * Compatibility @@ -94,7 +99,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) struct snd_pcm *pcm = substream->pcm; struct snd_pcm_str *pstr = substream->pstr; - snd_assert(substream != NULL, return -ENXIO); memset(info, 0, sizeof(*info)); info->card = pcm->card->number; info->device = pcm->device; @@ -138,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, #ifdef RULES_DEBUG #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -char *snd_pcm_hw_param_names[] = { +static const char * const snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), @@ -186,12 +190,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk("%s = ", snd_pcm_hw_param_names[k]); - printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); + pr_debug("%s = ", snd_pcm_hw_param_names[k]); + pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); #endif changed = snd_mask_refine(m, constrs_mask(constrs, k)); #ifdef RULES_DEBUG - printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); + pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); #endif if (changed) params->cmask |= 1 << k; @@ -206,21 +210,21 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk("%s = ", snd_pcm_hw_param_names[k]); + pr_debug("%s = ", snd_pcm_hw_param_names[k]); if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); - printk(" -> "); + pr_cont(" -> "); #endif changed = snd_interval_refine(i, constrs_interval(constrs, k)); #ifdef RULES_DEBUG if (i->empty) - printk("empty\n"); + pr_cont("empty\n"); else - printk("%c%u %u%c\n", + pr_cont("%c%u %u%c\n", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); #endif @@ -251,18 +255,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!doit) continue; #ifdef RULES_DEBUG - printk("Rule %d [%p]: ", k, r->func); + pr_debug("Rule %d [%p]: ", k, r->func); if (r->var >= 0) { - printk("%s = ", snd_pcm_hw_param_names[r->var]); + pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); if (hw_is_mask(r->var)) { m = hw_param_mask(params, r->var); - printk("%x", *m->bits); + pr_cont("%x", *m->bits); } else { i = hw_param_interval(params, r->var); if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } @@ -271,19 +275,19 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, changed = r->func(params, r); #ifdef RULES_DEBUG if (r->var >= 0) { - printk(" -> "); + pr_cont(" -> "); if (hw_is_mask(r->var)) - printk("%x", *m->bits); + pr_cont("%x", *m->bits); else { if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } } - printk("\n"); + pr_cont("\n"); #endif rstamps[k] = stamp; if (changed && r->var >= 0) { @@ -312,9 +316,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, hw = &substream->runtime->hw; if (!params->info) - params->info = hw->info; - if (!params->fifo_size) - params->fifo_size = hw->fifo_size; + params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; + if (!params->fifo_size) { + m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_min(m) == snd_mask_max(m) && + snd_interval_min(i) == snd_interval_max(i)) { + changed = substream->ops->ioctl(substream, + SNDRV_PCM_IOCTL1_FIFO_SIZE, params); + if (changed < 0) + return changed; + } + } params->rmask = 0; return 0; } @@ -327,21 +340,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(params, _params, sizeof(*params))) { - err = -EFAULT; - goto out; - } + params = memdup_user(_params, sizeof(*params)); + if (IS_ERR(params)) + return PTR_ERR(params); + err = snd_pcm_hw_refine(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; } -out: + kfree(params); return err; } @@ -361,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } +static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state) +{ + snd_pcm_stream_lock_irq(substream); + if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED) + substream->runtime->status->state = state; + snd_pcm_stream_unlock_irq(substream); +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -369,9 +385,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int bits; snd_pcm_uframes_t frames; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: @@ -383,7 +399,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, return -EBADFD; } snd_pcm_stream_unlock_irq(substream); -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) if (!substream->oss.oss) #endif if (atomic_read(&substream->mmap_count)) @@ -415,6 +431,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->info = params->info; runtime->rate_num = params->rate_num; runtime->rate_den = params->rate_den; + runtime->no_period_wakeup = + (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); bits = snd_pcm_format_physical_width(runtime->format); runtime->sample_bits = bits; @@ -441,19 +460,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->boundary *= 2; snd_pcm_timer_resolution_change(substream); - runtime->status->state = SNDRV_PCM_STATE_SETUP; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); if ((usecs = period_to_usecs(runtime)) >= 0) - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id, usecs); + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); return 0; _error: - /* hardware might be unuseable from this time, + /* hardware might be unusable from this time, so we force application to retry to set the correct hardware parameter settings */ - runtime->status->state = SNDRV_PCM_STATE_OPEN; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); return err; @@ -465,21 +484,16 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(params, _params, sizeof(*params))) { - err = -EFAULT; - goto out; - } + params = memdup_user(_params, sizeof(*params)); + if (IS_ERR(params)) + return PTR_ERR(params); + err = snd_pcm_hw_params(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; } -out: + kfree(params); return err; } @@ -489,9 +503,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime; int result = 0; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_SETUP: @@ -506,9 +520,8 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) return -EBADFD; if (substream->ops->hw_free) result = substream->ops->hw_free(substream); - runtime->status->state = SNDRV_PCM_STATE_OPEN; - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); + pm_qos_remove_request(&substream->latency_pm_qos_req); return result; } @@ -516,10 +529,11 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_pcm_stream_unlock_irq(substream); @@ -540,6 +554,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } + err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->period_step = params->period_step; @@ -553,10 +568,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - wake_up(&runtime->sleep); + err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); - return 0; + return err; } static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, @@ -587,6 +602,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->audio_tstamp = + runtime->status->audio_tstamp; goto _tstamp_end; } } @@ -597,14 +614,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { status->avail = snd_pcm_playback_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || - runtime->status->state == SNDRV_PCM_STATE_DRAINING) + runtime->status->state == SNDRV_PCM_STATE_DRAINING) { status->delay = runtime->buffer_size - status->avail; - else + status->delay += runtime->delay; + } else status->delay = 0; } else { status->avail = snd_pcm_capture_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - status->delay = status->avail; + status->delay = status->avail + runtime->delay; else status->delay = 0; } @@ -621,11 +639,8 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, struct snd_pcm_status __user * _status) { struct snd_pcm_status status; - struct snd_pcm_runtime *runtime; int res; - snd_assert(substream != NULL, return -ENXIO); - runtime = substream->runtime; memset(&status, 0, sizeof(status)); res = snd_pcm_status(substream, &status); if (res < 0) @@ -641,7 +656,6 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime; unsigned int channel; - snd_assert(substream != NULL, return -ENXIO); channel = info->channel; runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); @@ -862,6 +876,9 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); + runtime->hw_ptr_jiffies = jiffies; + runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / + runtime->rate; runtime->status->state = state; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -879,10 +896,10 @@ static struct action_ops snd_pcm_action_start = { }; /** - * snd_pcm_start + * snd_pcm_start - start all linked streams * @substream: the PCM substream instance * - * Start all linked streams. + * Return: Zero if successful, or a negative error code. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -921,6 +938,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) runtime->status->state = state; } wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_stop = { @@ -930,14 +948,15 @@ static struct action_ops snd_pcm_action_stop = { }; /** - * snd_pcm_stop + * snd_pcm_stop - try to stop all running streams in the substream group * @substream: the PCM substream instance * @state: PCM state after stopping the stream * - * Try to stop all running streams in the substream group. - * The state of each stream is changed to the given value after that unconditionally. + * The state of each stream is then changed to the given state unconditionally. + * + * Return: Zero if successful, or a negative error code. */ -int snd_pcm_stop(struct snd_pcm_substream *substream, int state) +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } @@ -945,12 +964,13 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, int state) EXPORT_SYMBOL(snd_pcm_stop); /** - * snd_pcm_drain_done + * snd_pcm_drain_done - stop the DMA only when the given stream is playback * @substream: the PCM substream * - * Stop the DMA only when the given stream is playback. - * The state is changed to SETUP. + * After stopping, the state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. + * + * Return: Zero if succesful, or a negative error code. */ int snd_pcm_drain_done(struct snd_pcm_substream *substream) { @@ -979,6 +999,15 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push) { if (substream->runtime->trigger_master != substream) return 0; + /* some drivers might use hw_ptr to recover from the pause - + update the hw_ptr now */ + if (push) + snd_pcm_update_hw_ptr(substream); + /* The jiffies check in snd_pcm_update_hw_ptr*() is done by + * a delta between the current jiffies, this gives a large enough + * delta, effectively to skip the check once. + */ + substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000; return substream->ops->trigger(substream, push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : SNDRV_PCM_TRIGGER_PAUSE_RELEASE); @@ -1003,6 +1032,7 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; if (substream->timer) @@ -1060,6 +1090,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_suspend = { @@ -1069,11 +1100,13 @@ static struct action_ops snd_pcm_action_suspend = { }; /** - * snd_pcm_suspend + * snd_pcm_suspend - trigger SUSPEND to all linked streams * @substream: the PCM substream * - * Trigger SUSPEND to all linked streams. * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @substream is %NULL), or a negative error + * code. */ int snd_pcm_suspend(struct snd_pcm_substream *substream) { @@ -1092,11 +1125,12 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) EXPORT_SYMBOL(snd_pcm_suspend); /** - * snd_pcm_suspend_all + * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm * @pcm: the PCM instance * - * Trigger SUSPEND to all substreams in the given pcm. * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @pcm is %NULL), or a negative error code. */ int snd_pcm_suspend_all(struct snd_pcm *pcm) { @@ -1249,7 +1283,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; - // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); runtime->hw_ptr_base = 0; runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; @@ -1308,7 +1341,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; runtime->control->appl_ptr = runtime->status->hw_ptr; - runtime->status->state = SNDRV_PCM_STATE_PREPARED; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED); } static struct action_ops snd_pcm_action_prepare = { @@ -1318,11 +1351,11 @@ static struct action_ops snd_pcm_action_prepare = { }; /** - * snd_pcm_prepare + * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags * - * Prepare the PCM substream to be triggerable. + * Return: Zero if successful, or a negative error code. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) @@ -1350,9 +1383,14 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) { - if (substream->f_flags & O_NONBLOCK) - return -EAGAIN; - substream->runtime->trigger_master = substream; + struct snd_pcm_runtime *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_DISCONNECTED: + case SNDRV_PCM_STATE_SUSPENDED: + return -EBADFD; + } + runtime->trigger_master = substream; return 0; } @@ -1371,6 +1409,9 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) case SNDRV_PCM_STATE_RUNNING: runtime->status->state = SNDRV_PCM_STATE_DRAINING; break; + case SNDRV_PCM_STATE_XRUN: + runtime->status->state = SNDRV_PCM_STATE_SETUP; + break; default: break; } @@ -1396,12 +1437,6 @@ static struct action_ops snd_pcm_action_drain_init = { .post_action = snd_pcm_post_drain_init }; -struct drain_rec { - struct snd_pcm_substream *substream; - wait_queue_t wait; - snd_pcm_uframes_t stop_threshold; -}; - static int snd_pcm_drop(struct snd_pcm_substream *substream); /* @@ -1411,16 +1446,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream); * After this call, all streams are supposed to be either SETUP or DRAINING * (capture only) state. */ -static int snd_pcm_drain(struct snd_pcm_substream *substream) +static int snd_pcm_drain(struct snd_pcm_substream *substream, + struct file *file) { struct snd_card *card; struct snd_pcm_runtime *runtime; struct snd_pcm_substream *s; + wait_queue_t wait; int result = 0; - int i, num_drecs; - struct drain_rec *drec, drec_tmp, *d; + int nonblock = 0; - snd_assert(substream != NULL, return -ENXIO); card = substream->pcm->card; runtime = substream->runtime; @@ -1436,75 +1471,78 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) } } - /* allocate temporary record for drain sync */ - down_read(&snd_pcm_link_rwsem); - if (snd_pcm_stream_linked(substream)) { - drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL); - if (! drec) { - up_read(&snd_pcm_link_rwsem); - snd_power_unlock(card); - return -ENOMEM; - } - } else - drec = &drec_tmp; - - /* count only playback streams */ - num_drecs = 0; - snd_pcm_group_for_each_entry(s, substream) { - runtime = s->runtime; - if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - d = &drec[num_drecs++]; - d->substream = s; - init_waitqueue_entry(&d->wait, current); - add_wait_queue(&runtime->sleep, &d->wait); - /* stop_threshold fixup to avoid endless loop when - * stop_threshold > buffer_size - */ - d->stop_threshold = runtime->stop_threshold; - if (runtime->stop_threshold > runtime->buffer_size) - runtime->stop_threshold = runtime->buffer_size; - } - } - up_read(&snd_pcm_link_rwsem); + if (file) { + if (file->f_flags & O_NONBLOCK) + nonblock = 1; + } else if (substream->f_flags & O_NONBLOCK) + nonblock = 1; + down_read(&snd_pcm_link_rwsem); snd_pcm_stream_lock_irq(substream); /* resume pause */ - if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) snd_pcm_pause(substream, 0); /* pre-start/stop - all running streams are changed to DRAINING state */ result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); - if (result < 0) { - snd_pcm_stream_unlock_irq(substream); - goto _error; + if (result < 0) + goto unlock; + /* in non-blocking, we don't wait in ioctl but let caller poll */ + if (nonblock) { + result = -EAGAIN; + goto unlock; } for (;;) { long tout; + struct snd_pcm_runtime *to_check; if (signal_pending(current)) { result = -ERESTARTSYS; break; } - /* all finished? */ - for (i = 0; i < num_drecs; i++) { - runtime = drec[i].substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) + /* find a substream to drain */ + to_check = NULL; + snd_pcm_group_for_each_entry(s, substream) { + if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; + runtime = s->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + to_check = runtime; break; + } } - if (i == num_drecs) - break; /* yes, all drained */ - - set_current_state(TASK_INTERRUPTIBLE); + if (!to_check) + break; /* all drained */ + init_waitqueue_entry(&wait, current); + add_wait_queue(&to_check->sleep, &wait); snd_pcm_stream_unlock_irq(substream); + up_read(&snd_pcm_link_rwsem); snd_power_unlock(card); - tout = schedule_timeout(10 * HZ); + if (runtime->no_period_wakeup) + tout = MAX_SCHEDULE_TIMEOUT; + else { + tout = 10; + if (runtime->rate) { + long t = runtime->period_size * 2 / runtime->rate; + tout = max(t, tout); + } + tout = msecs_to_jiffies(tout * 1000); + } + tout = schedule_timeout_interruptible(tout); snd_power_lock(card); + down_read(&snd_pcm_link_rwsem); snd_pcm_stream_lock_irq(substream); + remove_wait_queue(&to_check->sleep, &wait); + if (card->shutdown) { + result = -ENODEV; + break; + } if (tout == 0) { if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) result = -ESTRPIPE; else { - snd_printd("playback drain error (DMA or IRQ trouble?)\n"); + dev_dbg(substream->pcm->card->dev, + "playback drain error (DMA or IRQ trouble?)\n"); snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); result = -EIO; } @@ -1512,18 +1550,9 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) } } + unlock: snd_pcm_stream_unlock_irq(substream); - - _error: - for (i = 0; i < num_drecs; i++) { - d = &drec[i]; - runtime = d->substream->runtime; - remove_wait_queue(&runtime->sleep, &d->wait); - runtime->stop_threshold = d->stop_threshold; - } - - if (drec != &drec_tmp) - kfree(drec); + up_read(&snd_pcm_link_rwsem); snd_power_unlock(card); return result; @@ -1537,24 +1566,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) static int snd_pcm_drop(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - struct snd_card *card; int result = 0; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - card = substream->pcm->card; if (runtime->status->state == SNDRV_PCM_STATE_OPEN || - runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) return -EBADFD; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto _unlock; - } - snd_pcm_stream_lock_irq(substream); /* resume pause */ if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) @@ -1563,35 +1585,21 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); /* runtime->control->appl_ptr = runtime->status->hw_ptr; */ snd_pcm_stream_unlock_irq(substream); - _unlock: - snd_power_unlock(card); + return result; } -/* WARNING: Don't forget to fput back the file */ -static struct file *snd_pcm_file_fd(int fd) +static bool is_pcm_file(struct file *file) { - struct file *file; - struct inode *inode; + struct inode *inode = file_inode(file); unsigned int minor; - file = fget(fd); - if (!file) - return NULL; - inode = file->f_path.dentry->d_inode; - if (!S_ISCHR(inode->i_mode) || - imajor(inode) != snd_major) { - fput(file); - return NULL; - } + if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major) + return false; 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); - return NULL; - } - return file; + return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) || + snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE); } /* @@ -1600,15 +1608,24 @@ static struct file *snd_pcm_file_fd(int fd) static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) { int res = 0; - struct file *file; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; + struct snd_pcm_group *group; + struct fd f = fdget(fd); - file = snd_pcm_file_fd(fd); - if (!file) + if (!f.file) return -EBADFD; - pcm_file = file->private_data; + if (!is_pcm_file(f.file)) { + res = -EBADFD; + goto _badf; + } + pcm_file = f.file->private_data; substream1 = pcm_file->substream; + group = kmalloc(sizeof(*group), GFP_KERNEL); + if (!group) { + res = -ENOMEM; + goto _nolock; + } down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || @@ -1621,11 +1638,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) goto _end; } if (!snd_pcm_stream_linked(substream)) { - substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); - if (substream->group == NULL) { - res = -ENOMEM; - goto _end; - } + substream->group = group; + group = NULL; spin_lock_init(&substream->group->lock); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); @@ -1637,7 +1651,11 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) _end: write_unlock_irq(&snd_pcm_link_rwlock); up_write(&snd_pcm_link_rwsem); - fput(file); + _nolock: + snd_card_unref(substream1->pcm->card); + kfree(group); + _badf: + fdput(f); return res; } @@ -1940,33 +1958,41 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; } err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, hw->channels_min, hw->channels_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, hw->rate_min, hw->rate_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, hw->period_bytes_min, hw->period_bytes_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, hw->periods_min, hw->periods_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, hw->period_bytes_min, hw->buffer_bytes_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_rule_buffer_bytes_max, substream, @@ -1977,7 +2003,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) /* FIXME: remove */ if (runtime->dma_bytes) { err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; } if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { @@ -2012,6 +2039,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) substream->ops->close(substream); substream->hw_opened = 0; } + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); if (substream->pcm_release) { substream->pcm_release(substream); substream->pcm_release = NULL; @@ -2038,7 +2067,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, err = snd_pcm_hw_constraints_init(substream); if (err < 0) { - snd_printd("snd_pcm_hw_constraints_init failed\n"); + pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n"); goto error; } @@ -2049,7 +2078,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, err = snd_pcm_hw_constraints_complete(substream); if (err < 0) { - snd_printd("snd_pcm_hw_constraints_complete failed\n"); + pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n"); goto error; } @@ -2065,17 +2094,12 @@ EXPORT_SYMBOL(snd_pcm_open_substream); static int snd_pcm_open_file(struct file *file, struct snd_pcm *pcm, - int stream, - struct snd_pcm_file **rpcm_file) + int stream) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; - struct snd_pcm_str *str; int err; - snd_assert(rpcm_file != NULL, return -EINVAL); - *rpcm_file = NULL; - err = snd_pcm_open_substream(pcm, stream, file, &substream); if (err < 0) return err; @@ -2087,37 +2111,45 @@ static int snd_pcm_open_file(struct file *file, } pcm_file->substream = substream; if (substream->ref_count == 1) { - str = substream->pstr; substream->file = pcm_file; substream->pcm_release = pcm_release_private; } file->private_data = pcm_file; - *rpcm_file = pcm_file; + return 0; } static int snd_pcm_playback_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - + int err = nonseekable_open(inode, file); + if (err < 0) + return err; pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK); - return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); + err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); + if (pcm) + snd_card_unref(pcm->card); + return err; } static int snd_pcm_capture_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - + int err = nonseekable_open(inode, file); + if (err < 0) + return err; pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_CAPTURE); - return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); + err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); + if (pcm) + snd_card_unref(pcm->card); + return err; } static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) { int err; - struct snd_pcm_file *pcm_file; wait_queue_t wait; if (pcm == NULL) { @@ -2135,7 +2167,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) add_wait_queue(&pcm->open_wait, &wait); mutex_lock(&pcm->open_mutex); while (1) { - err = snd_pcm_open_file(file, pcm, stream, &pcm_file); + err = snd_pcm_open_file(file, pcm, stream); if (err >= 0) break; if (err == -EAGAIN) { @@ -2149,6 +2181,10 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) mutex_unlock(&pcm->open_mutex); schedule(); mutex_lock(&pcm->open_mutex); + if (pcm->card->shutdown) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; @@ -2176,9 +2212,9 @@ static int snd_pcm_release(struct inode *inode, struct file *file) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (snd_BUG_ON(!substream)) + return -ENXIO; pcm = substream->pcm; - fasync_helper(-1, file, 0, &substream->runtime->fasync); mutex_lock(&pcm->open_mutex); snd_pcm_release_substream(substream); kfree(pcm_file); @@ -2212,6 +2248,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2257,6 +2296,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2303,6 +2345,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2349,6 +2394,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2381,6 +2429,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2413,6 +2462,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2424,6 +2474,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, n = snd_pcm_playback_hw_avail(runtime); else n = snd_pcm_capture_avail(runtime); + n += runtime->delay; break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -2499,8 +2550,6 @@ static int snd_pcm_common_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - switch (cmd) { case SNDRV_PCM_IOCTL_PVERSION: return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; @@ -2549,7 +2598,7 @@ static int snd_pcm_common_ioctl1(struct file *file, return snd_pcm_hw_params_old_user(substream, arg); #endif case SNDRV_PCM_IOCTL_DRAIN: - return snd_pcm_drain(substream); + return snd_pcm_drain(substream, file); case SNDRV_PCM_IOCTL_DROP: return snd_pcm_drop(substream); case SNDRV_PCM_IOCTL_PAUSE: @@ -2561,7 +2610,7 @@ static int snd_pcm_common_ioctl1(struct file *file, return res; } } - snd_printd("unknown ioctl = 0x%x\n", cmd); + pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } @@ -2569,8 +2618,10 @@ static int snd_pcm_playback_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + if (snd_BUG_ON(!substream)) + return -ENXIO; + if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) + return -EINVAL; switch (cmd) { case SNDRV_PCM_IOCTL_WRITEI_FRAMES: { @@ -2603,13 +2654,11 @@ static int snd_pcm_playback_ioctl1(struct file *file, return -EFAULT; if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); - if (bufs == NULL) - return -ENOMEM; - if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { - kfree(bufs); - return -EFAULT; - } + + bufs = memdup_user(xfern.bufs, + sizeof(void *) * runtime->channels); + if (IS_ERR(bufs)) + return PTR_ERR(bufs); result = snd_pcm_lib_writev(substream, bufs, xfern.frames); kfree(bufs); __put_user(result, &_xfern->result); @@ -2649,8 +2698,10 @@ static int snd_pcm_capture_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); + if (snd_BUG_ON(!substream)) + return -ENXIO; + if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; switch (cmd) { case SNDRV_PCM_IOCTL_READI_FRAMES: { @@ -2683,13 +2734,11 @@ static int snd_pcm_capture_ioctl1(struct file *file, return -EFAULT; if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); - if (bufs == NULL) - return -ENOMEM; - if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { - kfree(bufs); - return -EFAULT; - } + + bufs = memdup_user(xfern.bufs, + sizeof(void *) * runtime->channels); + if (IS_ERR(bufs)) + return PTR_ERR(bufs); result = snd_pcm_lib_readv(substream, bufs, xfern.frames); kfree(bufs); __put_user(result, &_xfern->result); @@ -2789,7 +2838,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2812,21 +2862,17 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, result = -ENXIO; goto end); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { - result = -EBADFD; - goto end; - } - if (!frame_aligned(runtime, count)) { - result = -EINVAL; - goto end; - } + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!frame_aligned(runtime, count)) + return -EINVAL; count = bytes_to_frames(runtime, count); result = snd_pcm_lib_write(substream, buf, count); if (result > 0) result = frames_to_bytes(runtime, result); - end: return result; } @@ -2844,7 +2890,8 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, pcm_file = iocb->ki_filp->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2878,17 +2925,14 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, pcm_file = iocb->ki_filp->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, result = -ENXIO; goto end); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { - result = -EBADFD; - goto end; - } + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; if (nr_segs > 128 || nr_segs != runtime->channels || - !frame_aligned(runtime, iov->iov_len)) { - result = -EINVAL; - goto end; - } + !frame_aligned(runtime, iov->iov_len)) + return -EINVAL; frames = bytes_to_samples(runtime, iov->iov_len); bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); if (bufs == NULL) @@ -2899,7 +2943,6 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, if (result > 0) result = frames_to_bytes(runtime, result); kfree(bufs); - end: return result; } @@ -2914,7 +2957,8 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); @@ -2952,7 +2996,8 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); @@ -3009,7 +3054,7 @@ static int snd_pcm_mmap_status_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_status = +static const struct vm_operations_struct snd_pcm_vm_ops_status = { .fault = snd_pcm_mmap_status_fault, }; @@ -3017,18 +3062,15 @@ static struct vm_operations_struct snd_pcm_vm_ops_status = static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { - struct snd_pcm_runtime *runtime; long size; if (!(area->vm_flags & VM_READ)) return -EINVAL; - runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))) 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; } @@ -3049,7 +3091,7 @@ static int snd_pcm_mmap_control_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_control = +static const struct vm_operations_struct snd_pcm_vm_ops_control = { .fault = snd_pcm_mmap_control_fault, }; @@ -3057,18 +3099,15 @@ static struct vm_operations_struct snd_pcm_vm_ops_control = static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { - struct snd_pcm_runtime *runtime; long size; if (!(area->vm_flags & VM_READ)) return -EINVAL; - runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))) 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 */ @@ -3087,6 +3126,27 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file } #endif /* coherent mmap */ +static inline struct page * +snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) +{ + void *vaddr = substream->runtime->dma_area + ofs; +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return virt_to_page(CAC_ADDR(vaddr)); +#endif +#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) { + dma_addr_t addr = substream->runtime->dma_addr + ofs; + addr -= get_dma_offset(substream->dma_buffer.dev.dev); + /* assume dma_handle set via pfn_to_phys() in + * mm/dma-noncoherent.c + */ + return pfn_to_page(addr >> PAGE_SHIFT); + } +#endif + return virt_to_page(vaddr); +} + /* * fault callback for mmapping a RAM page */ @@ -3097,7 +3157,6 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area, struct snd_pcm_runtime *runtime; unsigned long offset; struct page * page; - void *vaddr; size_t dma_bytes; if (substream == NULL) @@ -3107,69 +3166,80 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area, dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) return VM_FAULT_SIGBUS; - if (substream->ops->page) { + if (substream->ops->page) page = substream->ops->page(substream, offset); - if (!page) - return VM_FAULT_SIGBUS; - } else { - vaddr = runtime->dma_area + offset; - page = virt_to_page(vaddr); - } + else + page = snd_pcm_default_page_ops(substream, offset); + if (!page) + return VM_FAULT_SIGBUS; get_page(page); vmf->page = page; return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_data = -{ +static const struct vm_operations_struct snd_pcm_vm_ops_data = { + .open = snd_pcm_mmap_data_open, + .close = snd_pcm_mmap_data_close, +}; + +static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { .open = snd_pcm_mmap_data_open, .close = snd_pcm_mmap_data_close, .fault = snd_pcm_mmap_data_fault, }; +#ifndef ARCH_HAS_DMA_MMAP_COHERENT +/* This should be defined / handled globally! */ +#ifdef CONFIG_ARM +#define ARCH_HAS_DMA_MMAP_COHERENT +#endif +#endif + /* * mmap the DMA buffer on RAM */ -static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_ops = &snd_pcm_vm_ops_data; - area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; - atomic_inc(&substream->mmap_count); +int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } +#endif /* CONFIG_GENERIC_ALLOCATOR */ +#ifdef ARCH_HAS_DMA_MMAP_COHERENT + if (!substream->ops->page && + substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return dma_mmap_coherent(substream->dma_buffer.dev.dev, + area, + substream->runtime->dma_area, + substream->runtime->dma_addr, + area->vm_end - area->vm_start); +#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV && + !plat_device_is_coherent(substream->dma_buffer.dev.dev)) + area->vm_page_prot = pgprot_noncached(area->vm_page_prot); +#endif /* ARCH_HAS_DMA_MMAP_COHERENT */ + /* mmap with fault handler */ + area->vm_ops = &snd_pcm_vm_ops_data_fault; return 0; } +EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap); /* * mmap the DMA buffer on I/O memory area */ #if SNDRV_PCM_INFO_MMAP_IOMEM -static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = -{ - .open = snd_pcm_mmap_data_open, - .close = snd_pcm_mmap_data_close, -}; - int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area) { - long size; - unsigned long offset; + struct snd_pcm_runtime *runtime = substream->runtime;; -#ifdef pgprot_noncached area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif - area->vm_ops = &snd_pcm_vm_ops_data_mmio; - area->vm_private_data = substream; - area->vm_flags |= VM_IO; - size = area->vm_end - area->vm_start; - offset = area->vm_pgoff << PAGE_SHIFT; - if (io_remap_pfn_range(area, area->vm_start, - (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, - size, area->vm_page_prot)) - return -EAGAIN; - atomic_inc(&substream->mmap_count); - return 0; + return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); @@ -3185,6 +3255,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, long size; unsigned long offset; size_t dma_bytes; + int err; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (!(area->vm_flags & (VM_WRITE|VM_READ))) @@ -3194,7 +3265,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, return -EINVAL; } runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) @@ -3210,10 +3280,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, if (offset > dma_bytes - size) return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_data; + area->vm_private_data = substream; if (substream->ops->mmap) - return substream->ops->mmap(substream, area); + err = substream->ops->mmap(substream, area); else - return snd_pcm_default_mmap(substream, area); + err = snd_pcm_lib_default_mmap(substream, area); + if (!err) + atomic_inc(&substream->mmap_count); + return err; } EXPORT_SYMBOL(snd_pcm_mmap_data); @@ -3226,7 +3301,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { @@ -3249,17 +3325,13 @@ static int snd_pcm_fasync(int fd, struct file * file, int on) struct snd_pcm_file * pcm_file; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; - int err; pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - - err = fasync_helper(fd, file, on, &runtime->fasync); - if (err < 0) - return err; - return 0; + return fasync_helper(fd, file, on, &runtime->fasync); } /* @@ -3325,18 +3397,12 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, int err; params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); - if (!oparams) { - err = -ENOMEM; - goto out; - } + if (!params) + return -ENOMEM; - if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { - err = -EFAULT; + oparams = memdup_user(_oparams, sizeof(*oparams)); + if (IS_ERR(oparams)) { + err = PTR_ERR(oparams); goto out; } snd_pcm_hw_convert_from_old_params(params, oparams); @@ -3346,9 +3412,10 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, if (!err) err = -EFAULT; } + + kfree(oparams); out: kfree(params); - kfree(oparams); return err; } @@ -3360,17 +3427,12 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, int err; params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); - if (!oparams) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { - err = -EFAULT; + if (!params) + return -ENOMEM; + + oparams = memdup_user(_oparams, sizeof(*oparams)); + if (IS_ERR(oparams)) { + err = PTR_ERR(oparams); goto out; } snd_pcm_hw_convert_from_old_params(params, oparams); @@ -3380,13 +3442,39 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, if (!err) err = -EFAULT; } + + kfree(oparams); out: kfree(params); - kfree(oparams); return err; } #endif /* CONFIG_SND_SUPPORT_OLD_API */ +#ifndef CONFIG_MMU +static unsigned long snd_pcm_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + struct snd_pcm_file *pcm_file = file->private_data; + struct snd_pcm_substream *substream = pcm_file->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long offset = pgoff << PAGE_SHIFT; + + switch (offset) { + case SNDRV_PCM_MMAP_OFFSET_STATUS: + return (unsigned long)runtime->status; + case SNDRV_PCM_MMAP_OFFSET_CONTROL: + return (unsigned long)runtime->control; + default: + return (unsigned long)runtime->dma_area + offset; + } +} +#else +# define snd_pcm_get_unmapped_area NULL +#endif + /* * Register section */ @@ -3398,11 +3486,13 @@ const struct file_operations snd_pcm_f_ops[2] = { .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, + .llseek = no_llseek, .poll = snd_pcm_playback_poll, .unlocked_ioctl = snd_pcm_playback_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, @@ -3410,10 +3500,12 @@ const struct file_operations snd_pcm_f_ops[2] = { .aio_read = snd_pcm_aio_read, .open = snd_pcm_capture_open, .release = snd_pcm_release, + .llseek = no_llseek, .poll = snd_pcm_capture_poll, .unlocked_ioctl = snd_pcm_capture_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, } }; diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 033a024d153..20ecd8f1808 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -20,6 +20,7 @@ */ #include <linux/time.h> +#include <linux/gcd.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/timer.h> @@ -28,22 +29,6 @@ * Timer functions */ -/* Greatest common divisor */ -static unsigned long gcd(unsigned long a, unsigned long b) -{ - unsigned long r; - if (a < b) { - r = a; - a = b; - b = r; - } - while ((r = a % b) != 0) { - a = b; - b = r; - } - return b; -} - void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) { unsigned long rate, mult, fsize, l, post; @@ -51,12 +36,14 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) mult = 1000000000; rate = runtime->rate; - snd_assert(rate != 0, return); + if (snd_BUG_ON(!rate)) + return; l = gcd(mult, rate); mult /= l; rate /= l; fsize = runtime->period_size; - snd_assert(fsize != 0, return); + if (snd_BUG_ON(!fsize)) + return; l = gcd(rate, fsize); rate /= l; fsize /= l; @@ -66,7 +53,9 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) post *= 2; } if (rate == 0) { - snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size); + pcm_err(substream->pcm, + "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", + runtime->rate, runtime->period_size); runtime->timer_resolution = -1; return; } @@ -83,25 +72,19 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) static int snd_pcm_timer_start(struct snd_timer * timer) { - unsigned long flags; struct snd_pcm_substream *substream; substream = snd_timer_chip(timer); - spin_lock_irqsave(&substream->timer_lock, flags); substream->timer_running = 1; - spin_unlock_irqrestore(&substream->timer_lock, flags); return 0; } static int snd_pcm_timer_stop(struct snd_timer * timer) { - unsigned long flags; struct snd_pcm_substream *substream; substream = snd_timer_chip(timer); - spin_lock_irqsave(&substream->timer_lock, flags); substream->timer_running = 0; - spin_unlock_irqrestore(&substream->timer_lock, flags); return 0; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index f7ea7287c59..6fc71a4c8a5 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -27,7 +27,7 @@ #include <linux/time.h> #include <linux/wait.h> #include <linux/mutex.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/delay.h> #include <sound/rawmidi.h> #include <sound/info.h> @@ -56,6 +56,13 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device); static LIST_HEAD(snd_rawmidi_devices); static DEFINE_MUTEX(register_mutex); +#define rmidi_err(rmidi, fmt, args...) \ + dev_err((rmidi)->card->dev, fmt, ##args) +#define rmidi_warn(rmidi, fmt, args...) \ + dev_warn((rmidi)->card->dev, fmt, ##args) +#define rmidi_dbg(rmidi, fmt, args...) \ + dev_dbg((rmidi)->card->dev, fmt, ##args) + static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device) { struct snd_rawmidi *rawmidi; @@ -92,16 +99,12 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre (!substream->append || runtime->avail >= count); } -static void snd_rawmidi_input_event_tasklet(unsigned long data) -{ - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->runtime->event(substream); -} - -static void snd_rawmidi_output_trigger_tasklet(unsigned long data) +static void snd_rawmidi_input_event_work(struct work_struct *work) { - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->ops->trigger(substream, 1); + struct snd_rawmidi_runtime *runtime = + container_of(work, struct snd_rawmidi_runtime, event_work); + if (runtime->event) + runtime->event(runtime->substream); } static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) @@ -110,16 +113,10 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL) return -ENOMEM; + runtime->substream = substream; spin_lock_init(&runtime->lock); init_waitqueue_head(&runtime->sleep); - if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) - tasklet_init(&runtime->tasklet, - snd_rawmidi_input_event_tasklet, - (unsigned long)substream); - else - tasklet_init(&runtime->tasklet, - snd_rawmidi_output_trigger_tasklet, - (unsigned long)substream); + INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work); runtime->event = NULL; runtime->buffer_size = PAGE_SIZE; runtime->avail_min = 1; @@ -148,19 +145,18 @@ static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream) static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up) { - if (up) { - tasklet_hi_schedule(&substream->runtime->tasklet); - } else { - tasklet_kill(&substream->runtime->tasklet); - substream->ops->trigger(substream, 0); - } + if (!substream->opened) + return; + substream->ops->trigger(substream, up); } static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) { + if (!substream->opened) + return; substream->ops->trigger(substream, up); - if (!up && substream->runtime->event) - tasklet_kill(&substream->runtime->tasklet); + if (!up) + cancel_work_sync(&substream->runtime->event_work); } int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) @@ -176,6 +172,7 @@ int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) spin_unlock_irqrestore(&runtime->lock, flags); return 0; } +EXPORT_SYMBOL(snd_rawmidi_drop_output); int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) { @@ -191,7 +188,9 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) if (signal_pending(current)) err = -ERESTARTSYS; if (runtime->avail < runtime->buffer_size && !timeout) { - snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size); + rmidi_warn(substream->rmidi, + "rawmidi drain error (avail = %li, buffer_size = %li)\n", + (long)runtime->avail, (long)runtime->buffer_size); err = -EIO; } runtime->drain = 0; @@ -205,6 +204,7 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) } return err; } +EXPORT_SYMBOL(snd_rawmidi_drain_output); int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) { @@ -219,159 +219,145 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) spin_unlock_irqrestore(&runtime->lock, flags); return 0; } +EXPORT_SYMBOL(snd_rawmidi_drain_input); -int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice, - int mode, struct snd_rawmidi_file * rfile) +/* look for an available substream for the given stream direction; + * if a specific subdevice is given, try to assign it + */ +static int assign_substream(struct snd_rawmidi *rmidi, int subdevice, + int stream, int mode, + struct snd_rawmidi_substream **sub_ret) { - struct snd_rawmidi *rmidi; - struct list_head *list1, *list2; - struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL; - struct snd_rawmidi_runtime *input = NULL, *output = NULL; - int err; + struct snd_rawmidi_substream *substream; + struct snd_rawmidi_str *s = &rmidi->streams[stream]; + static unsigned int info_flags[2] = { + [SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT, + [SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT, + }; - if (rfile) - rfile->input = rfile->output = NULL; - mutex_lock(®ister_mutex); - rmidi = snd_rawmidi_search(card, device); - mutex_unlock(®ister_mutex); - if (rmidi == NULL) { - err = -ENODEV; - goto __error1; - } - if (!try_module_get(rmidi->card->module)) { - err = -EFAULT; - goto __error1; - } - if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) - mutex_lock(&rmidi->open_mutex); - if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { - if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { - err = -ENXIO; - goto __error; - } - if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { - err = -ENODEV; - goto __error; - } - if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >= - rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { - err = -EAGAIN; - goto __error; - } - } - if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { - if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { - err = -ENXIO; - goto __error; - } - if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { - err = -ENODEV; - goto __error; - } - if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >= - rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { - err = -EAGAIN; - goto __error; + if (!(rmidi->info_flags & info_flags[stream])) + return -ENXIO; + if (subdevice >= 0 && subdevice >= s->substream_count) + return -ENODEV; + + list_for_each_entry(substream, &s->substreams, list) { + if (substream->opened) { + if (stream == SNDRV_RAWMIDI_STREAM_INPUT || + !(mode & SNDRV_RAWMIDI_LFLG_APPEND) || + !substream->append) + continue; } - } - list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next; - while (1) { - if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { - sinput = NULL; - if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { - err = -EAGAIN; - goto __error; - } - break; + if (subdevice < 0 || subdevice == substream->number) { + *sub_ret = substream; + return 0; } - sinput = list_entry(list1, struct snd_rawmidi_substream, list); - if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened) - goto __nexti; - if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number)) - break; - __nexti: - list1 = list1->next; } - list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next; - while (1) { - if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { - soutput = NULL; - if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { - err = -EAGAIN; - goto __error; - } - break; - } - soutput = list_entry(list2, struct snd_rawmidi_substream, list); - if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { - if (mode & SNDRV_RAWMIDI_LFLG_APPEND) { - if (soutput->opened && !soutput->append) - goto __nexto; - } else { - if (soutput->opened) - goto __nexto; - } + return -EAGAIN; +} + +/* open and do ref-counting for the given substream */ +static int open_substream(struct snd_rawmidi *rmidi, + struct snd_rawmidi_substream *substream, + int mode) +{ + int err; + + if (substream->use_count == 0) { + err = snd_rawmidi_runtime_create(substream); + if (err < 0) + return err; + err = substream->ops->open(substream); + if (err < 0) { + snd_rawmidi_runtime_free(substream); + return err; } - if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number)) - break; - __nexto: - list2 = list2->next; + substream->opened = 1; + substream->active_sensing = 0; + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) + substream->append = 1; + substream->pid = get_pid(task_pid(current)); + rmidi->streams[substream->stream].substream_opened++; } + substream->use_count++; + return 0; +} + +static void close_substream(struct snd_rawmidi *rmidi, + struct snd_rawmidi_substream *substream, + int cleanup); + +static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode, + struct snd_rawmidi_file *rfile) +{ + struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL; + int err; + + rfile->input = rfile->output = NULL; if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { - if ((err = snd_rawmidi_runtime_create(sinput)) < 0) - goto __error; - input = sinput->runtime; - if ((err = sinput->ops->open(sinput)) < 0) - goto __error; - sinput->opened = 1; - rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++; - } else { - sinput = NULL; + err = assign_substream(rmidi, subdevice, + SNDRV_RAWMIDI_STREAM_INPUT, + mode, &sinput); + if (err < 0) + return err; } if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { - if (soutput->opened) - goto __skip_output; - if ((err = snd_rawmidi_runtime_create(soutput)) < 0) { - if (mode & SNDRV_RAWMIDI_LFLG_INPUT) - sinput->ops->close(sinput); - goto __error; - } - output = soutput->runtime; - if ((err = soutput->ops->open(soutput)) < 0) { - if (mode & SNDRV_RAWMIDI_LFLG_INPUT) - sinput->ops->close(sinput); - goto __error; - } - __skip_output: - soutput->opened = 1; - if (mode & SNDRV_RAWMIDI_LFLG_APPEND) - soutput->append = 1; - if (soutput->use_count++ == 0) - soutput->active_sensing = 1; - rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++; - } else { - soutput = NULL; + err = assign_substream(rmidi, subdevice, + SNDRV_RAWMIDI_STREAM_OUTPUT, + mode, &soutput); + if (err < 0) + return err; } - if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) - mutex_unlock(&rmidi->open_mutex); - if (rfile) { - rfile->rmidi = rmidi; - rfile->input = sinput; - rfile->output = soutput; + + if (sinput) { + err = open_substream(rmidi, sinput, mode); + if (err < 0) + return err; + } + if (soutput) { + err = open_substream(rmidi, soutput, mode); + if (err < 0) { + if (sinput) + close_substream(rmidi, sinput, 0); + return err; + } } + + rfile->rmidi = rmidi; + rfile->input = sinput; + rfile->output = soutput; return 0; +} - __error: - if (input != NULL) - snd_rawmidi_runtime_free(sinput); - if (output != NULL) - snd_rawmidi_runtime_free(soutput); - module_put(rmidi->card->module); - if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) - mutex_unlock(&rmidi->open_mutex); - __error1: +/* called from sound/core/seq/seq_midi.c */ +int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice, + int mode, struct snd_rawmidi_file * rfile) +{ + struct snd_rawmidi *rmidi; + int err; + + if (snd_BUG_ON(!rfile)) + return -EINVAL; + + mutex_lock(®ister_mutex); + rmidi = snd_rawmidi_search(card, device); + if (rmidi == NULL) { + mutex_unlock(®ister_mutex); + return -ENODEV; + } + if (!try_module_get(rmidi->card->module)) { + mutex_unlock(®ister_mutex); + return -ENXIO; + } + mutex_unlock(®ister_mutex); + + mutex_lock(&rmidi->open_mutex); + err = rawmidi_open_priv(rmidi, subdevice, mode, rfile); + mutex_unlock(&rmidi->open_mutex); + if (err < 0) + module_put(rmidi->card->module); return err; } +EXPORT_SYMBOL(snd_rawmidi_kernel_open); static int snd_rawmidi_open(struct inode *inode, struct file *file) { @@ -381,10 +367,17 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) unsigned short fflags; int err; struct snd_rawmidi *rmidi; - struct snd_rawmidi_file *rawmidi_file; + struct snd_rawmidi_file *rawmidi_file = NULL; wait_queue_t wait; struct snd_ctl_file *kctl; + if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) + return -EINVAL; /* invalid combination */ + + err = nonseekable_open(inode, file); + if (err < 0) + return err; + if (maj == snd_major) { rmidi = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_RAWMIDI); @@ -398,37 +391,39 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) if (rmidi == NULL) return -ENODEV; - if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) - return -EINVAL; /* invalid combination */ + + if (!try_module_get(rmidi->card->module)) { + snd_card_unref(rmidi->card); + return -ENXIO; + } + + mutex_lock(&rmidi->open_mutex); card = rmidi->card; err = snd_card_file_add(card, file); if (err < 0) - return -ENODEV; + goto __error_card; fflags = snd_rawmidi_file_flags(file); if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */ fflags |= SNDRV_RAWMIDI_LFLG_APPEND; - fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK; rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL); if (rawmidi_file == NULL) { - snd_card_file_remove(card, file); - return -ENOMEM; + err = -ENOMEM; + goto __error; } init_waitqueue_entry(&wait, current); add_wait_queue(&rmidi->open_wait, &wait); - mutex_lock(&rmidi->open_mutex); while (1) { subdevice = -1; - down_read(&card->controls_rwsem); + read_lock(&card->ctl_files_rwlock); list_for_each_entry(kctl, &card->ctl_files, list) { - if (kctl->pid == current->pid) { + if (kctl->pid == task_pid(current)) { subdevice = kctl->prefer_rawmidi_subdevice; if (subdevice != -1) break; } } - up_read(&card->controls_rwsem); - err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device, - subdevice, fflags, rawmidi_file); + read_unlock(&card->ctl_files_rwlock); + err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file); if (err >= 0) break; if (err == -EAGAIN) { @@ -442,89 +437,121 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) mutex_unlock(&rmidi->open_mutex); schedule(); mutex_lock(&rmidi->open_mutex); + if (rmidi->card->shutdown) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; } } + remove_wait_queue(&rmidi->open_wait, &wait); + if (err < 0) { + kfree(rawmidi_file); + goto __error; + } #ifdef CONFIG_SND_OSSEMUL if (rawmidi_file->input && rawmidi_file->input->runtime) rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); if (rawmidi_file->output && rawmidi_file->output->runtime) rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); #endif - remove_wait_queue(&rmidi->open_wait, &wait); - if (err >= 0) { - file->private_data = rawmidi_file; - } else { - snd_card_file_remove(card, file); - kfree(rawmidi_file); - } + file->private_data = rawmidi_file; mutex_unlock(&rmidi->open_mutex); + snd_card_unref(rmidi->card); + return 0; + + __error: + snd_card_file_remove(card, file); + __error_card: + mutex_unlock(&rmidi->open_mutex); + module_put(rmidi->card->module); + snd_card_unref(rmidi->card); return err; } -int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile) +static void close_substream(struct snd_rawmidi *rmidi, + struct snd_rawmidi_substream *substream, + int cleanup) { - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *substream; - struct snd_rawmidi_runtime *runtime; + if (--substream->use_count) + return; - snd_assert(rfile != NULL, return -ENXIO); - snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); - rmidi = rfile->rmidi; - mutex_lock(&rmidi->open_mutex); - if (rfile->input != NULL) { - substream = rfile->input; - rfile->input = NULL; - runtime = substream->runtime; - snd_rawmidi_input_trigger(substream, 0); - substream->ops->close(substream); - if (runtime->private_free != NULL) - runtime->private_free(substream); - snd_rawmidi_runtime_free(substream); - substream->opened = 0; - rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--; - } - if (rfile->output != NULL) { - substream = rfile->output; - rfile->output = NULL; - if (--substream->use_count == 0) { - runtime = substream->runtime; + if (cleanup) { + if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) + snd_rawmidi_input_trigger(substream, 0); + else { if (substream->active_sensing) { unsigned char buf = 0xfe; - /* sending single active sensing message to shut the device up */ + /* sending single active sensing message + * to shut the device up + */ snd_rawmidi_kernel_write(substream, &buf, 1); } if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) snd_rawmidi_output_trigger(substream, 0); - substream->ops->close(substream); - if (runtime->private_free != NULL) - runtime->private_free(substream); - snd_rawmidi_runtime_free(substream); - substream->opened = 0; - substream->append = 0; } - rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--; } + substream->ops->close(substream); + if (substream->runtime->private_free) + substream->runtime->private_free(substream); + snd_rawmidi_runtime_free(substream); + substream->opened = 0; + substream->append = 0; + put_pid(substream->pid); + substream->pid = NULL; + rmidi->streams[substream->stream].substream_opened--; +} + +static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) +{ + struct snd_rawmidi *rmidi; + + rmidi = rfile->rmidi; + mutex_lock(&rmidi->open_mutex); + if (rfile->input) { + close_substream(rmidi, rfile->input, 1); + rfile->input = NULL; + } + if (rfile->output) { + close_substream(rmidi, rfile->output, 1); + rfile->output = NULL; + } + rfile->rmidi = NULL; mutex_unlock(&rmidi->open_mutex); + wake_up(&rmidi->open_wait); +} + +/* called from sound/core/seq/seq_midi.c */ +int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) +{ + struct snd_rawmidi *rmidi; + + if (snd_BUG_ON(!rfile)) + return -ENXIO; + + rmidi = rfile->rmidi; + rawmidi_release_priv(rfile); module_put(rmidi->card->module); return 0; } +EXPORT_SYMBOL(snd_rawmidi_kernel_release); static int snd_rawmidi_release(struct inode *inode, struct file *file) { struct snd_rawmidi_file *rfile; struct snd_rawmidi *rmidi; - int err; + struct module *module; rfile = file->private_data; - err = snd_rawmidi_kernel_release(rfile); rmidi = rfile->rmidi; - wake_up(&rmidi->open_wait); + rawmidi_release_priv(rfile); kfree(rfile); + module = rmidi->card->module; snd_card_file_remove(rmidi->card, file); - return err; + module_put(module); + return 0; } static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, @@ -586,6 +613,7 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info } return -ENXIO; } +EXPORT_SYMBOL(snd_rawmidi_info_select); static int snd_rawmidi_info_select_user(struct snd_card *card, struct snd_rawmidi_info __user *_info) @@ -621,10 +649,10 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = kmalloc(params->buffer_size, GFP_KERNEL); + newbuf = krealloc(runtime->buffer, params->buffer_size, + GFP_KERNEL); if (!newbuf) return -ENOMEM; - kfree(runtime->buffer); runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; runtime->avail = runtime->buffer_size; @@ -633,6 +661,7 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, substream->active_sensing = !params->no_active_sensing; return 0; } +EXPORT_SYMBOL(snd_rawmidi_output_params); int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params * params) @@ -648,16 +677,17 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = kmalloc(params->buffer_size, GFP_KERNEL); + newbuf = krealloc(runtime->buffer, params->buffer_size, + GFP_KERNEL); if (!newbuf) return -ENOMEM; - kfree(runtime->buffer); runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; } runtime->avail_min = params->avail_min; return 0; } +EXPORT_SYMBOL(snd_rawmidi_input_params); static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, struct snd_rawmidi_status * status) @@ -789,10 +819,9 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long return -EINVAL; } } -#ifdef CONFIG_SND_DEBUG default: - snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd); -#endif + rmidi_dbg(rfile->rmidi, + "rawmidi: unknown command = 0x%x\n", cmd); } return -ENOTTY; } @@ -811,6 +840,8 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)argp)) return -EFAULT; + if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */ + device = SNDRV_RAWMIDI_DEVICES - 1; mutex_lock(®ister_mutex); device = device < 0 ? 0 : device + 1; while (device < SNDRV_RAWMIDI_DEVICES) { @@ -848,7 +879,7 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, * * Reads the data from the internal buffer. * - * Returns the size of read data, or a negative error code on failure. + * Return: The size of read data, or a negative error code on failure. */ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, const unsigned char *buffer, int count) @@ -857,8 +888,11 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, int result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; + if (!substream->opened) + return -EBADFD; if (runtime->buffer == NULL) { - snd_printd("snd_rawmidi_receive: input is not active!!!\n"); + rmidi_dbg(substream->rmidi, + "snd_rawmidi_receive: input is not active!!!\n"); return -EINVAL; } spin_lock_irqsave(&runtime->lock, flags); @@ -902,13 +936,14 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, } if (result > 0) { if (runtime->event) - tasklet_hi_schedule(&runtime->tasklet); + schedule_work(&runtime->event_work); else if (snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } spin_unlock_irqrestore(&runtime->lock, flags); return result; } +EXPORT_SYMBOL(snd_rawmidi_receive); static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, unsigned char __user *userbuf, @@ -951,6 +986,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, snd_rawmidi_input_trigger(substream, 1); return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count); } +EXPORT_SYMBOL(snd_rawmidi_kernel_read); static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count, loff_t *offset) @@ -982,6 +1018,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun spin_unlock_irq(&runtime->lock); schedule(); remove_wait_queue(&runtime->sleep, &wait); + if (rfile->rmidi->card->shutdown) + return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; if (!runtime->avail) @@ -1005,8 +1043,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun /** * snd_rawmidi_transmit_empty - check whether the output buffer is empty * @substream: the rawmidi substream - * - * Returns 1 if the internal output buffer is empty, 0 if not. + * + * Return: 1 if the internal output buffer is empty, 0 if not. */ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) { @@ -1015,7 +1053,8 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) unsigned long flags; if (runtime->buffer == NULL) { - snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n"); + rmidi_dbg(substream->rmidi, + "snd_rawmidi_transmit_empty: output is not active!!!\n"); return 1; } spin_lock_irqsave(&runtime->lock, flags); @@ -1023,6 +1062,7 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) spin_unlock_irqrestore(&runtime->lock, flags); return result; } +EXPORT_SYMBOL(snd_rawmidi_transmit_empty); /** * snd_rawmidi_transmit_peek - copy data from the internal buffer @@ -1036,7 +1076,7 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * - * Returns the size of copied data, or a negative error code on failure. + * Return: The size of copied data, or a negative error code on failure. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) @@ -1046,7 +1086,8 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, struct snd_rawmidi_runtime *runtime = substream->runtime; if (runtime->buffer == NULL) { - snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n"); + rmidi_dbg(substream->rmidi, + "snd_rawmidi_transmit_peek: output is not active!!!\n"); return -EINVAL; } result = 0; @@ -1078,17 +1119,18 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&runtime->lock, flags); return result; } +EXPORT_SYMBOL(snd_rawmidi_transmit_peek); /** * snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream - * @count: the tranferred count + * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * - * Returns the advanced size if successful, or a negative error code on failure. + * Return: The advanced size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { @@ -1096,11 +1138,12 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) struct snd_rawmidi_runtime *runtime = substream->runtime; if (runtime->buffer == NULL) { - snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n"); + rmidi_dbg(substream->rmidi, + "snd_rawmidi_transmit_ack: output is not active!!!\n"); return -EINVAL; } spin_lock_irqsave(&runtime->lock, flags); - snd_assert(runtime->avail + count <= runtime->buffer_size, ); + snd_BUG_ON(runtime->avail + count > runtime->buffer_size); runtime->hw_ptr += count; runtime->hw_ptr %= runtime->buffer_size; runtime->avail += count; @@ -1112,6 +1155,7 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) spin_unlock_irqrestore(&runtime->lock, flags); return count; } +EXPORT_SYMBOL(snd_rawmidi_transmit_ack); /** * snd_rawmidi_transmit - copy from the buffer to the device @@ -1121,16 +1165,19 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) * * Copies data from the buffer to the device and advances the pointer. * - * Returns the copied size if successful, or a negative error code on failure. + * Return: The copied size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { + if (!substream->opened) + return -EBADFD; count = snd_rawmidi_transmit_peek(substream, buffer, count); if (count < 0) return count; return snd_rawmidi_transmit_ack(substream, count); } +EXPORT_SYMBOL(snd_rawmidi_transmit); static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, const unsigned char __user *userbuf, @@ -1141,8 +1188,10 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, long count1, result; struct snd_rawmidi_runtime *runtime = substream->runtime; - snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL); - snd_assert(runtime->buffer != NULL, return -EINVAL); + if (snd_BUG_ON(!kernelbuf && !userbuf)) + return -EINVAL; + if (snd_BUG_ON(!runtime->buffer)) + return -EINVAL; result = 0; spin_lock_irqsave(&runtime->lock, flags); @@ -1190,6 +1239,7 @@ long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream, { return snd_rawmidi_kernel_write1(substream, NULL, buf, count); } +EXPORT_SYMBOL(snd_rawmidi_kernel_write); static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) @@ -1221,6 +1271,8 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, spin_unlock_irq(&runtime->lock); timeout = schedule_timeout(30 * HZ); remove_wait_queue(&runtime->sleep, &wait); + if (rfile->rmidi->card->shutdown) + return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; if (!runtime->avail && !timeout) @@ -1237,7 +1289,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, break; count -= count1; } - if (file->f_flags & O_SYNC) { + if (file->f_flags & O_DSYNC) { spin_lock_irq(&runtime->lock); while (runtime->avail != runtime->buffer_size) { wait_queue_t wait; @@ -1319,6 +1371,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, substream->number, (unsigned long) substream->bytes); if (substream->opened) { + snd_iprintf(buffer, + " Owner PID : %d\n", + pid_vnr(substream->pid)); runtime = substream->runtime; snd_iprintf(buffer, " Mode : %s\n" @@ -1340,6 +1395,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, substream->number, (unsigned long) substream->bytes); if (substream->opened) { + snd_iprintf(buffer, + " Owner PID : %d\n", + pid_vnr(substream->pid)); runtime = substream->runtime; snd_iprintf(buffer, " Buffer size : %lu\n" @@ -1365,6 +1423,7 @@ static const struct file_operations snd_rawmidi_f_ops = .write = snd_rawmidi_write, .open = snd_rawmidi_open, .release = snd_rawmidi_release, + .llseek = no_llseek, .poll = snd_rawmidi_poll, .unlocked_ioctl = snd_rawmidi_ioctl, .compat_ioctl = snd_rawmidi_ioctl_compat, @@ -1381,7 +1440,7 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, for (idx = 0; idx < count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); if (substream == NULL) { - snd_printk(KERN_ERR "rawmidi: cannot allocate substream\n"); + rmidi_err(rmidi, "rawmidi: cannot allocate substream\n"); return -ENOMEM; } substream->stream = direction; @@ -1406,7 +1465,7 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, * Creates a new rawmidi instance. * Use snd_rawmidi_set_ops() to set the operators to the new instance. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_rawmidi_new(struct snd_card *card, char *id, int device, int output_count, int input_count, @@ -1420,12 +1479,13 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, .dev_disconnect = snd_rawmidi_dev_disconnect, }; - snd_assert(rrawmidi != NULL, return -EINVAL); - *rrawmidi = NULL; - snd_assert(card != NULL, return -ENXIO); + if (snd_BUG_ON(!card)) + return -ENXIO; + if (rrawmidi) + *rrawmidi = NULL; rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); if (rmidi == NULL) { - snd_printk(KERN_ERR "rawmidi: cannot allocate\n"); + dev_err(card->dev, "rawmidi: cannot allocate\n"); return -ENOMEM; } rmidi->card = card; @@ -1455,9 +1515,11 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, snd_rawmidi_free(rmidi); return err; } - *rrawmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; return 0; } +EXPORT_SYMBOL(snd_rawmidi_new); static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream) { @@ -1472,7 +1534,8 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream) static int snd_rawmidi_free(struct snd_rawmidi *rmidi) { - snd_assert(rmidi != NULL, return -ENXIO); + if (!rmidi) + return 0; snd_info_free_entry(rmidi->proc_entry); rmidi->proc_entry = NULL; @@ -1522,7 +1585,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device) if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device, &snd_rawmidi_f_ops, rmidi, name)) < 0) { - snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); + rmidi_err(rmidi, "unable to register rawmidi device %i:%i\n", + rmidi->card->number, rmidi->device); list_del(&rmidi->list); mutex_unlock(®ister_mutex); return err; @@ -1539,8 +1603,10 @@ static int snd_rawmidi_dev_register(struct snd_device *device) if ((int)rmidi->device == midi_map[rmidi->card->number]) { if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0, &snd_rawmidi_f_ops, - rmidi, name) < 0) { - snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); + rmidi) < 0) { + rmidi_err(rmidi, + "unable to register OSS rawmidi device %i:%i\n", + rmidi->card->number, 0); } else { rmidi->ossreg++; #ifdef SNDRV_OSS_INFO_DEV_MIDI @@ -1551,8 +1617,10 @@ static int snd_rawmidi_dev_register(struct snd_device *device) if ((int)rmidi->device == amidi_map[rmidi->card->number]) { if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1, &snd_rawmidi_f_ops, - rmidi, name) < 0) { - snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); + rmidi) < 0) { + rmidi_err(rmidi, + "unable to register OSS rawmidi device %i:%i\n", + rmidi->card->number, 1); } else { rmidi->ossreg++; } @@ -1586,9 +1654,20 @@ static int snd_rawmidi_dev_register(struct snd_device *device) static int snd_rawmidi_dev_disconnect(struct snd_device *device) { struct snd_rawmidi *rmidi = device->device_data; + int dir; mutex_lock(®ister_mutex); + mutex_lock(&rmidi->open_mutex); + wake_up(&rmidi->open_wait); list_del_init(&rmidi->list); + for (dir = 0; dir < 2; dir++) { + struct snd_rawmidi_substream *s; + list_for_each_entry(s, &rmidi->streams[dir].substreams, list) { + if (s->runtime) + wake_up(&s->runtime->sleep); + } + } + #ifdef CONFIG_SND_OSSEMUL if (rmidi->ossreg) { if ((int)rmidi->device == midi_map[rmidi->card->number]) { @@ -1603,6 +1682,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) } #endif /* CONFIG_SND_OSSEMUL */ snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + mutex_unlock(&rmidi->open_mutex); mutex_unlock(®ister_mutex); return 0; } @@ -1623,6 +1703,7 @@ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, list_for_each_entry(substream, &rmidi->streams[stream].substreams, list) substream->ops = ops; } +EXPORT_SYMBOL(snd_rawmidi_set_ops); /* * ENTRY functions @@ -1638,11 +1719,13 @@ static int __init alsa_rawmidi_init(void) /* check device map table */ for (i = 0; i < SNDRV_CARDS; i++) { if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { - snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]); + pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n", + i, midi_map[i]); midi_map[i] = 0; } if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { - snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]); + pr_err("ALSA: rawmidi: invalid amidi_map[%d] = %d\n", + i, amidi_map[i]); amidi_map[i] = 1; } } @@ -1659,21 +1742,3 @@ static void __exit alsa_rawmidi_exit(void) module_init(alsa_rawmidi_init) module_exit(alsa_rawmidi_exit) - -EXPORT_SYMBOL(snd_rawmidi_output_params); -EXPORT_SYMBOL(snd_rawmidi_input_params); -EXPORT_SYMBOL(snd_rawmidi_drop_output); -EXPORT_SYMBOL(snd_rawmidi_drain_output); -EXPORT_SYMBOL(snd_rawmidi_drain_input); -EXPORT_SYMBOL(snd_rawmidi_receive); -EXPORT_SYMBOL(snd_rawmidi_transmit_empty); -EXPORT_SYMBOL(snd_rawmidi_transmit_peek); -EXPORT_SYMBOL(snd_rawmidi_transmit_ack); -EXPORT_SYMBOL(snd_rawmidi_transmit); -EXPORT_SYMBOL(snd_rawmidi_new); -EXPORT_SYMBOL(snd_rawmidi_set_ops); -EXPORT_SYMBOL(snd_rawmidi_info_select); -EXPORT_SYMBOL(snd_rawmidi_kernel_open); -EXPORT_SYMBOL(snd_rawmidi_kernel_release); -EXPORT_SYMBOL(snd_rawmidi_kernel_read); -EXPORT_SYMBOL(snd_rawmidi_kernel_write); diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c index 97b30fb4c36..f3420d11a12 100644 --- a/sound/core/rtctimer.c +++ b/sound/core/rtctimer.c @@ -22,12 +22,12 @@ #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/log2.h> #include <sound/core.h> #include <sound/timer.h> -#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) +#if IS_ENABLED(CONFIG_RTC) #include <linux/mc146818rtc.h> @@ -91,7 +91,8 @@ static int rtctimer_start(struct snd_timer *timer) { rtc_task_t *rtc = timer->private_data; - snd_assert(rtc != NULL, return -EINVAL); + if (snd_BUG_ON(!rtc)) + return -EINVAL; rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); rtc_control(rtc, RTC_PIE_ON, 0); return 0; @@ -101,7 +102,8 @@ static int rtctimer_stop(struct snd_timer *timer) { rtc_task_t *rtc = timer->private_data; - snd_assert(rtc != NULL, return -EINVAL); + if (snd_BUG_ON(!rtc)) + return -EINVAL; rtc_control(rtc, RTC_PIE_OFF, 0); return 0; } @@ -116,7 +118,7 @@ static void rtctimer_tasklet(unsigned long data) */ static void rtctimer_interrupt(void *private_data) { - tasklet_hi_schedule(private_data); + tasklet_schedule(private_data); } @@ -130,8 +132,7 @@ static int __init rtctimer_init(void) if (rtctimer_freq < 2 || rtctimer_freq > 8192 || !is_power_of_2(rtctimer_freq)) { - snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", - rtctimer_freq); + pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq); return -EINVAL; } @@ -183,4 +184,4 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC)); -#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ +#endif /* IS_ENABLED(CONFIG_RTC) */ diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig new file mode 100644 index 00000000000..b851fd890a8 --- /dev/null +++ b/sound/core/seq/Kconfig @@ -0,0 +1,16 @@ +# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX) + +config SND_RAWMIDI_SEQ + def_tristate SND_SEQUENCER && SND_RAWMIDI + +config SND_OPL3_LIB_SEQ + def_tristate SND_SEQUENCER && SND_OPL3_LIB + +config SND_OPL4_LIB_SEQ + def_tristate SND_SEQUENCER && SND_OPL4_LIB + +config SND_SBAWE_SEQ + def_tristate SND_SEQUENCER && SND_SBAWE + +config SND_EMU10K1_SEQ + def_tristate SND_SEQUENCER && SND_EMU10K1 diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 069593717fb..941f64a853e 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -3,10 +3,6 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) - obj-$(CONFIG_SND_SEQUENCER) += oss/ -endif - snd-seq-device-objs := seq_device.o snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ seq_fifo.o seq_prioq.o seq_timer.o \ @@ -17,24 +13,17 @@ snd-seq-midi-event-objs := seq_midi_event.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# <empty string> - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) - obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) -obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o + obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o + obj-$(CONFIG_SND_SEQUENCER) += oss/ endif obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o # Toplevel Module Dependency obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o -obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o -obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o +obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o +obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o +obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 777796e9449..16d42679e43 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -21,7 +21,7 @@ */ #include <linux/init.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/minors.h> @@ -39,12 +39,6 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC); -#ifdef SNDRV_SEQ_OSS_DEBUG -module_param(seq_oss_debug, int, 0644); -MODULE_PARM_DESC(seq_oss_debug, "debug option"); -int seq_oss_debug = 0; -#endif - /* * prototypes @@ -164,7 +158,8 @@ odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { struct seq_oss_devinfo *dp; dp = file->private_data; - snd_assert(dp != NULL, return -EIO); + if (snd_BUG_ON(!dp)) + return -ENXIO; return snd_seq_oss_read(dp, buf, count); } @@ -174,7 +169,8 @@ odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offs { struct seq_oss_devinfo *dp; dp = file->private_data; - snd_assert(dp != NULL, return -EIO); + if (snd_BUG_ON(!dp)) + return -ENXIO; return snd_seq_oss_write(dp, buf, count, file); } @@ -183,7 +179,8 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct seq_oss_devinfo *dp; dp = file->private_data; - snd_assert(dp != NULL, return -EIO); + if (snd_BUG_ON(!dp)) + return -ENXIO; return snd_seq_oss_ioctl(dp, cmd, arg); } @@ -198,7 +195,8 @@ odev_poll(struct file *file, poll_table * wait) { struct seq_oss_devinfo *dp; dp = file->private_data; - snd_assert(dp != NULL, return 0); + if (snd_BUG_ON(!dp)) + return -ENXIO; return snd_seq_oss_poll(dp, file, wait); } @@ -216,6 +214,7 @@ static const struct file_operations seq_oss_f_ops = .poll = odev_poll, .unlocked_ioctl = odev_ioctl, .compat_ioctl = odev_ioctl_compat, + .llseek = noop_llseek, }; static int __init @@ -226,22 +225,19 @@ register_device(void) mutex_lock(®ister_mutex); if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0, - &seq_oss_f_ops, NULL, - SNDRV_SEQ_OSS_DEVNAME)) < 0) { - snd_printk(KERN_ERR "can't register device seq\n"); + &seq_oss_f_ops, NULL)) < 0) { + pr_err("ALSA: seq_oss: can't register device seq\n"); mutex_unlock(®ister_mutex); return rc; } if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0, - &seq_oss_f_ops, NULL, - SNDRV_SEQ_OSS_DEVNAME)) < 0) { - snd_printk(KERN_ERR "can't register device music\n"); + &seq_oss_f_ops, NULL)) < 0) { + pr_err("ALSA: seq_oss: can't register device music\n"); snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0); mutex_unlock(®ister_mutex); return rc; } - debug_printk(("device registered\n")); mutex_unlock(®ister_mutex); return 0; } @@ -250,11 +246,10 @@ static void unregister_device(void) { mutex_lock(®ister_mutex); - debug_printk(("device unregistered\n")); if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) - snd_printk(KERN_ERR "error unregister device music\n"); + pr_err("ALSA: seq_oss: error unregister device music\n"); if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0) - snd_printk(KERN_ERR "error unregister device seq\n"); + pr_err("ALSA: seq_oss: error unregister device seq\n"); mutex_unlock(®ister_mutex); } diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index bf8d2b4cb15..b4392432524 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -31,9 +31,6 @@ #include <sound/seq_kernel.h> #include <sound/info.h> -/* enable debug print */ -#define SNDRV_SEQ_OSS_DEBUG - /* max. applications */ #define SNDRV_SEQ_OSS_MAX_CLIENTS 16 #define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16 @@ -46,7 +43,6 @@ #define SNDRV_SEQ_OSS_VERSION_STR "0.1.8" /* device and proc interface name */ -#define SNDRV_SEQ_OSS_DEVNAME "seq_oss" #define SNDRV_SEQ_OSS_PROCNAME "oss" @@ -177,13 +173,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, /* misc. functions for proc interface */ char *enabled_str(int bool); - -/* for debug */ -#ifdef SNDRV_SEQ_OSS_DEBUG -extern int seq_oss_debug; -#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0) -#else -#define debug_printk(x) /**/ -#endif - #endif /* __SEQ_OSS_DEVICE_H */ diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c index 066f5f3e3f4..c3908862bc8 100644 --- a/sound/core/seq/oss/seq_oss_event.c +++ b/sound/core/seq/oss/seq_oss_event.c @@ -285,7 +285,12 @@ local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev static int note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev) { - struct seq_oss_synthinfo *info = &dp->synths[dev]; + struct seq_oss_synthinfo *info; + + if (!snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + info = &dp->synths[dev]; switch (info->arg.event_passing) { case SNDRV_SEQ_OSS_PROCESS_EVENTS: if (! info->ch || ch < 0 || ch >= info->nr_voices) { @@ -340,7 +345,12 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st static int note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev) { - struct seq_oss_synthinfo *info = &dp->synths[dev]; + struct seq_oss_synthinfo *info; + + if (!snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + info = &dp->synths[dev]; switch (info->arg.event_passing) { case SNDRV_SEQ_OSS_PROCESS_EVENTS: if (! info->ch || ch < 0 || ch >= info->nr_voices) { diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index d0d721c22ea..b9184d20c39 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -28,7 +28,10 @@ #include "seq_oss_timer.h" #include "seq_oss_event.h" #include <linux/init.h> +#include <linux/export.h> #include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/workqueue.h> /* * common variables @@ -58,6 +61,14 @@ static void free_devinfo(void *private); #define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) +/* call snd_seq_oss_midi_lookup_ports() asynchronously */ +static void async_call_lookup_ports(struct work_struct *work) +{ + snd_seq_oss_midi_lookup_ports(system_client); +} + +static DECLARE_WORK(async_lookup_work, async_call_lookup_ports); + /* * create sequencer client for OSS sequencer */ @@ -81,10 +92,6 @@ snd_seq_oss_create_client(void) goto __error; system_client = rc; - debug_printk(("new client = %d\n", rc)); - - /* look up midi devices */ - snd_seq_oss_midi_lookup_ports(system_client); /* create annoucement receiver port */ memset(port, 0, sizeof(*port)); @@ -113,6 +120,9 @@ snd_seq_oss_create_client(void) } rc = 0; + /* look up midi devices */ + schedule_work(&async_lookup_work); + __error: kfree(port); return rc; @@ -158,6 +168,7 @@ receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic int snd_seq_oss_delete_client(void) { + cancel_work_sync(&async_lookup_work); if (system_client >= 0) snd_seq_delete_kernel_client(system_client); @@ -178,10 +189,9 @@ snd_seq_oss_open(struct file *file, int level) dp = kzalloc(sizeof(*dp), GFP_KERNEL); if (!dp) { - snd_printk(KERN_ERR "can't malloc device info\n"); + pr_err("ALSA: seq_oss: can't malloc device info\n"); return -ENOMEM; } - debug_printk(("oss_open: dp = %p\n", dp)); dp->cseq = system_client; dp->port = -1; @@ -194,7 +204,7 @@ snd_seq_oss_open(struct file *file, int level) dp->index = i; if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { - snd_printk(KERN_ERR "too many applications\n"); + pr_err("ALSA: seq_oss: too many applications\n"); rc = -ENOMEM; goto _error; } @@ -204,21 +214,19 @@ snd_seq_oss_open(struct file *file, int level) snd_seq_oss_midi_setup(dp); if (dp->synth_opened == 0 && dp->max_mididev == 0) { - /* snd_printk(KERN_ERR "no device found\n"); */ + /* pr_err("ALSA: seq_oss: no device found\n"); */ rc = -ENODEV; goto _error; } /* create port */ - debug_printk(("create new port\n")); rc = create_port(dp); if (rc < 0) { - snd_printk(KERN_ERR "can't create port\n"); + pr_err("ALSA: seq_oss: can't create port\n"); goto _error; } /* allocate queue */ - debug_printk(("allocate queue\n")); rc = alloc_seq_queue(dp); if (rc < 0) goto _error; @@ -235,7 +243,6 @@ snd_seq_oss_open(struct file *file, int level) dp->file_mode = translate_mode(file); /* initialize read queue */ - debug_printk(("initialize read queue\n")); if (is_read_mode(dp->file_mode)) { dp->readq = snd_seq_oss_readq_new(dp, maxqlen); if (!dp->readq) { @@ -245,7 +252,6 @@ snd_seq_oss_open(struct file *file, int level) } /* initialize write queue */ - debug_printk(("initialize write queue\n")); if (is_write_mode(dp->file_mode)) { dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); if (!dp->writeq) { @@ -255,14 +261,12 @@ snd_seq_oss_open(struct file *file, int level) } /* initialize timer */ - debug_printk(("initialize timer\n")); dp->timer = snd_seq_oss_timer_new(dp); if (!dp->timer) { - snd_printk(KERN_ERR "can't alloc timer\n"); + pr_err("ALSA: seq_oss: can't alloc timer\n"); rc = -ENOMEM; goto _error; } - debug_printk(("timer initialized\n")); /* set private data pointer */ file->private_data = dp; @@ -276,17 +280,13 @@ snd_seq_oss_open(struct file *file, int level) client_table[dp->index] = dp; num_clients++; - debug_printk(("open done\n")); return 0; _error: - snd_seq_oss_writeq_delete(dp->writeq); - snd_seq_oss_readq_delete(dp->readq); snd_seq_oss_synth_cleanup(dp); snd_seq_oss_midi_cleanup(dp); - delete_port(dp); delete_seq_queue(dp->queue); - kfree(dp); + delete_port(dp); return rc; } @@ -338,7 +338,6 @@ create_port(struct seq_oss_devinfo *dp) return rc; dp->port = port.addr.port; - debug_printk(("new port = %d\n", port.addr.port)); return 0; } @@ -349,10 +348,11 @@ create_port(struct seq_oss_devinfo *dp) static int delete_port(struct seq_oss_devinfo *dp) { - if (dp->port < 0) + if (dp->port < 0) { + kfree(dp); return 0; + } - debug_printk(("delete_port %i\n", dp->port)); return snd_seq_event_port_detach(dp->cseq, dp->port); } @@ -390,7 +390,7 @@ delete_seq_queue(int queue) qinfo.queue = queue; rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); if (rc < 0) - printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc); + pr_err("ALSA: seq_oss: unable to delete queue %d (%d)\n", queue, rc); return rc; } @@ -427,21 +427,16 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp) client_table[dp->index] = NULL; num_clients--; - debug_printk(("resetting..\n")); snd_seq_oss_reset(dp); - debug_printk(("cleaning up..\n")); snd_seq_oss_synth_cleanup(dp); snd_seq_oss_midi_cleanup(dp); /* clear slot */ - debug_printk(("releasing resource..\n")); queue = dp->queue; if (dp->port >= 0) delete_port(dp); delete_seq_queue(queue); - - debug_printk(("release done\n")); } @@ -455,7 +450,6 @@ snd_seq_oss_drain_write(struct seq_oss_devinfo *dp) return; if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && dp->writeq) { - debug_printk(("syncing..\n")); while (snd_seq_oss_writeq_sync(dp->writeq)) ; } diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c index 5ac701c903c..5b8520177b0 100644 --- a/sound/core/seq/oss/seq_oss_ioctl.c +++ b/sound/core/seq/oss/seq_oss_ioctl.c @@ -90,12 +90,10 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg); case SNDCTL_SEQ_PANIC: - debug_printk(("panic\n")); snd_seq_oss_reset(dp); return -EINVAL; case SNDCTL_SEQ_SYNC: - debug_printk(("sync\n")); if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) return 0; while (snd_seq_oss_writeq_sync(dp->writeq)) @@ -105,55 +103,45 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca return 0; case SNDCTL_SEQ_RESET: - debug_printk(("reset\n")); snd_seq_oss_reset(dp); return 0; case SNDCTL_SEQ_TESTMIDI: - debug_printk(("test midi\n")); if (get_user(dev, p)) return -EFAULT; return snd_seq_oss_midi_open(dp, dev, dp->file_mode); case SNDCTL_SEQ_GETINCOUNT: - debug_printk(("get in count\n")); if (dp->readq == NULL || ! is_read_mode(dp->file_mode)) return 0; return put_user(dp->readq->qlen, p) ? -EFAULT : 0; case SNDCTL_SEQ_GETOUTCOUNT: - debug_printk(("get out count\n")); if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) return 0; return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0; case SNDCTL_SEQ_GETTIME: - debug_printk(("get time\n")); return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0; case SNDCTL_SEQ_RESETSAMPLES: - debug_printk(("reset samples\n")); if (get_user(dev, p)) return -EFAULT; return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); case SNDCTL_SEQ_NRSYNTHS: - debug_printk(("nr synths\n")); return put_user(dp->max_synthdev, p) ? -EFAULT : 0; case SNDCTL_SEQ_NRMIDIS: - debug_printk(("nr midis\n")); return put_user(dp->max_mididev, p) ? -EFAULT : 0; case SNDCTL_SYNTH_MEMAVL: - debug_printk(("mem avail\n")); if (get_user(dev, p)) return -EFAULT; val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); return put_user(val, p) ? -EFAULT : 0; case SNDCTL_FM_4OP_ENABLE: - debug_printk(("4op\n")); if (get_user(dev, p)) return -EFAULT; snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); @@ -161,19 +149,15 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca case SNDCTL_SYNTH_INFO: case SNDCTL_SYNTH_ID: - debug_printk(("synth info\n")); return snd_seq_oss_synth_info_user(dp, arg); case SNDCTL_SEQ_OUTOFBAND: - debug_printk(("out of band\n")); return snd_seq_oss_oob_user(dp, arg); case SNDCTL_MIDI_INFO: - debug_printk(("midi info\n")); return snd_seq_oss_midi_info_user(dp, arg); case SNDCTL_SEQ_THRESHOLD: - debug_printk(("threshold\n")); if (! is_write_mode(dp->file_mode)) return 0; if (get_user(val, p)) @@ -186,7 +170,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca return 0; case SNDCTL_MIDI_PRETIME: - debug_printk(("pretime\n")); if (dp->readq == NULL || !is_read_mode(dp->file_mode)) return 0; if (get_user(val, p)) @@ -199,7 +182,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca return put_user(val, p) ? -EFAULT : 0; default: - debug_printk(("others\n")); if (! is_write_mode(dp->file_mode)) return -EIO; return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg); diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 0a711d2d04f..3a4569669ef 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <sound/asoundef.h> #include "seq_oss_midi.h" #include "seq_oss_readq.h" #include "seq_oss_timer.h" @@ -27,6 +28,7 @@ #include <sound/seq_midi_event.h> #include "../seq_lock.h" #include <linux/init.h> +#include <linux/slab.h> /* @@ -70,7 +72,7 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, * look up the existing ports * this looks a very exhausting job. */ -int __init +int snd_seq_oss_midi_lookup_ports(int client) { struct snd_seq_client_info *clinfo; @@ -151,7 +153,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) struct seq_oss_midi *mdev; unsigned long flags; - debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port)); /* the port must include generic midi */ if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) return 0; @@ -173,7 +174,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) * allocate midi info record */ if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { - snd_printk(KERN_ERR "can't malloc midi info\n"); + pr_err("ALSA: seq_oss: can't malloc midi info\n"); return -ENOMEM; } @@ -189,7 +190,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) /* create MIDI coder */ if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { - snd_printk(KERN_ERR "can't malloc midi coder\n"); + pr_err("ALSA: seq_oss: can't malloc midi coder\n"); kfree(mdev); return -ENOMEM; } @@ -404,7 +405,6 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) return 0; } - debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened)); memset(&subs, 0, sizeof(subs)); if (mdev->opened & PERM_WRITE) { subs.sender = dp->addr; @@ -468,7 +468,6 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) struct snd_seq_event ev; int c; - debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port)); memset(&ev, 0, sizeof(ev)); ev.dest.client = mdev->client; ev.dest.port = mdev->port; @@ -476,19 +475,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) ev.source.port = dp->port; if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { ev.type = SNDRV_SEQ_EVENT_SENSING; - snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */ + snd_seq_oss_dispatch(dp, &ev, 0, 0); } for (c = 0; c < 16; c++) { ev.type = SNDRV_SEQ_EVENT_CONTROLLER; ev.data.control.channel = c; - ev.data.control.param = 123; - snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ + ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF; + snd_seq_oss_dispatch(dp, &ev, 0, 0); if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { - ev.data.control.param = 121; - snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ + ev.data.control.param = + MIDI_CTL_RESET_CONTROLLERS; + snd_seq_oss_dispatch(dp, &ev, 0, 0); ev.type = SNDRV_SEQ_EVENT_PITCHBEND; ev.data.control.value = 0; - snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ + snd_seq_oss_dispatch(dp, &ev, 0, 0); } } } diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index f5de79f29f1..654d17a5023 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -25,6 +25,7 @@ #include <sound/seq_oss_legacy.h> #include "../seq_lock.h" #include <linux/wait.h> +#include <linux/slab.h> /* * constants @@ -47,12 +48,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen) struct seq_oss_readq *q; if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) { - snd_printk(KERN_ERR "can't malloc read queue\n"); + pr_err("ALSA: seq_oss: can't malloc read queue\n"); return NULL; } if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) { - snd_printk(KERN_ERR "can't malloc read queue buffer\n"); + pr_err("ALSA: seq_oss: can't malloc read queue buffer\n"); kfree(q); return NULL; } diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index ab570a0a618..701feb71b70 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -24,6 +24,8 @@ #include "seq_oss_midi.h" #include "../seq_lock.h" #include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> /* * constants @@ -104,7 +106,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) unsigned long flags; if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { - snd_printk(KERN_ERR "can't malloc synth info\n"); + pr_err("ALSA: seq_oss: can't malloc synth info\n"); return -ENOMEM; } rec->seq_device = -1; @@ -128,7 +130,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) if (i >= max_synth_devs) { if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { spin_unlock_irqrestore(®ister_lock, flags); - snd_printk(KERN_ERR "no more synth slot\n"); + pr_err("ALSA: seq_oss: no more synth slot\n"); kfree(rec); return -ENOMEM; } @@ -136,7 +138,6 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) } rec->seq_device = i; synth_devs[i] = rec; - debug_printk(("synth %s registered %d\n", rec->name, i)); spin_unlock_irqrestore(®ister_lock, flags); dev->driver_data = rec; #ifdef SNDRV_OSS_INFO_DEV_SYNTH @@ -161,7 +162,7 @@ snd_seq_oss_synth_unregister(struct snd_seq_device *dev) } if (index >= max_synth_devs) { spin_unlock_irqrestore(®ister_lock, flags); - snd_printk(KERN_ERR "can't unregister synth\n"); + pr_err("ALSA: seq_oss: can't unregister synth\n"); return -EINVAL; } synth_devs[index] = NULL; @@ -245,11 +246,15 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) info->nr_voices = rec->nr_voices; if (info->nr_voices > 0) { info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); - if (!info->ch) - BUG(); + if (!info->ch) { + pr_err("ALSA: seq_oss: Cannot malloc voices\n"); + rec->oper.close(&info->arg); + module_put(rec->oper.owner); + snd_use_lock_free(&rec->use_lock); + continue; + } reset_channels(info); } - debug_printk(("synth %d assigned\n", i)); info->opened++; rec->opened++; dp->synth_opened++; @@ -303,7 +308,8 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) struct seq_oss_synth *rec; struct seq_oss_synthinfo *info; - snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return); + if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) + return; for (i = 0; i < dp->max_synthdev; i++) { info = &dp->synths[i]; if (! info->opened) @@ -318,7 +324,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) if (rec == NULL) continue; if (rec->opened > 0) { - debug_printk(("synth %d closed\n", i)); rec->oper.close(&info->arg); module_put(rec->oper.owner); rec->opened = 0; @@ -397,7 +402,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) struct seq_oss_synth *rec; struct seq_oss_synthinfo *info; - snd_assert(dev >= 0 && dev < dp->max_synthdev, return); + if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev)) + return; info = &dp->synths[dev]; if (! info->opened) return; @@ -599,6 +605,9 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in { struct seq_oss_synth *rec; + if (dev < 0 || dev >= dp->max_synthdev) + return -ENXIO; + if (dp->synths[dev].is_midi) { struct midi_info minf; snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf); diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c index c440fdacec9..4f24ea9fad9 100644 --- a/sound/core/seq/oss/seq_oss_timer.c +++ b/sound/core/seq/oss/seq_oss_timer.c @@ -23,6 +23,7 @@ #include "seq_oss_timer.h" #include "seq_oss_event.h" #include <sound/seq_oss_legacy.h> +#include <linux/slab.h> /* */ @@ -232,7 +233,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use int value; if (cmd == SNDCTL_SEQ_CTRLRATE) { - debug_printk(("ctrl rate\n")); /* if *arg == 0, just return the current rate */ if (get_user(value, arg)) return -EFAULT; @@ -247,21 +247,16 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use switch (cmd) { case SNDCTL_TMR_START: - debug_printk(("timer start\n")); return snd_seq_oss_timer_start(timer); case SNDCTL_TMR_STOP: - debug_printk(("timer stop\n")); return snd_seq_oss_timer_stop(timer); case SNDCTL_TMR_CONTINUE: - debug_printk(("timer continue\n")); return snd_seq_oss_timer_continue(timer); case SNDCTL_TMR_TEMPO: - debug_printk(("timer tempo\n")); if (get_user(value, arg)) return -EFAULT; return snd_seq_oss_timer_tempo(timer, value); case SNDCTL_TMR_TIMEBASE: - debug_printk(("timer timebase\n")); if (get_user(value, arg)) return -EFAULT; if (value < MIN_OSS_TIMEBASE) @@ -275,7 +270,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use case SNDCTL_TMR_METRONOME: case SNDCTL_TMR_SELECT: case SNDCTL_TMR_SOURCE: - debug_printk(("timer XXX\n")); /* not supported */ return 0; } diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c index 21742485819..d50338bbc21 100644 --- a/sound/core/seq/oss/seq_oss_writeq.c +++ b/sound/core/seq/oss/seq_oss_writeq.c @@ -27,6 +27,7 @@ #include "../seq_lock.h" #include "../seq_clientmgr.h" #include <linux/wait.h> +#include <linux/slab.h> /* diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index ee0f8405ab3..71211056108 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -20,7 +20,8 @@ */ #include <linux/init.h> -#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/device.h> #include <sound/core.h> #include <sound/initval.h> @@ -32,6 +33,7 @@ #include "seq_timer.h" #include "seq_system.h" #include "seq_info.h" +#include <sound/minors.h> #include <sound/seq_device.h> #if defined(CONFIG_SND_SEQ_DUMMY_MODULE) @@ -43,7 +45,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL; int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE; int seq_default_timer_card = -1; int seq_default_timer_device = -#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT +#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT + SNDRV_TIMER_GLOBAL_HRTIMER +#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT) SNDRV_TIMER_GLOBAL_RTC #else SNDRV_TIMER_GLOBAL_SYSTEM @@ -71,6 +75,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe module_param(seq_default_timer_resolution, int, 0644); MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz."); +MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER); +MODULE_ALIAS("devname:snd/seq"); + /* * INIT PART */ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 47cfa5186e3..225c73152ee 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -22,6 +22,7 @@ */ #include <linux/init.h> +#include <linux/export.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/minors.h> @@ -122,7 +123,7 @@ static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client) static struct snd_seq_client *clientptr(int clientid) { if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { - snd_printd("Seq: oops. Trying to get pointer to client %d\n", + pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n", clientid); return NULL; } @@ -135,7 +136,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid) struct snd_seq_client *client; if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { - snd_printd("Seq: oops. Trying to get pointer to client %d\n", + pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n", clientid); return NULL; } @@ -148,7 +149,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid) return NULL; } spin_unlock_irqrestore(&clients_lock, flags); -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES if (!in_interrupt()) { static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS]; static char card_requested[SNDRV_CARDS]; @@ -266,7 +267,8 @@ static int seq_free_client1(struct snd_seq_client *client) { unsigned long flags; - snd_assert(client != NULL, return -EINVAL); + if (!client) + return 0; snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); spin_lock_irqsave(&clients_lock, flags); @@ -289,8 +291,8 @@ static void seq_free_client(struct snd_seq_client * client) mutex_lock(®ister_mutex); switch (client->type) { case NO_CLIENT: - snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", - client->number); + pr_warn("ALSA: seq: Trying to free unused client %d\n", + client->number); break; case USER_CLIENT: case KERNEL_CLIENT: @@ -299,7 +301,7 @@ static void seq_free_client(struct snd_seq_client * client) break; default: - snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", + pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); } mutex_unlock(®ister_mutex); @@ -317,6 +319,11 @@ static int snd_seq_open(struct inode *inode, struct file *file) int c, mode; /* client id */ struct snd_seq_client *client; struct snd_seq_user_client *user; + int err; + + err = nonseekable_open(inode, file); + if (err < 0) + return err; if (mutex_lock_interruptible(®ister_mutex)) return -ERESTARTSYS; @@ -403,7 +410,8 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, return -EFAULT; /* check client structures are in place */ - snd_assert(client != NULL, return -ENXIO); + if (snd_BUG_ON(!client)) + return -ENXIO; if (!client->accept_input || (fifo = client->data.user.fifo) == NULL) return -ENXIO; @@ -652,7 +660,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, int atomic, int hop) { struct snd_seq_subscribers *subs; - int err = 0, num_ev = 0; + int err, result = 0, num_ev = 0; struct snd_seq_event event_saved; struct snd_seq_client_port *src_port; struct snd_seq_port_subs_info *grp; @@ -677,8 +685,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client, subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL); err = snd_seq_deliver_single_event(client, event, 0, atomic, hop); - if (err < 0) - break; + if (err < 0) { + /* save first error that occurs and continue */ + if (!result) + result = err; + continue; + } num_ev++; /* restore original event record */ *event = event_saved; @@ -689,7 +701,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, up_read(&grp->list_mutex); *event = event_saved; /* restore */ snd_seq_port_unlock(src_port); - return (err < 0) ? err : num_ev; + return (result < 0) ? result : num_ev; } @@ -701,7 +713,7 @@ static int port_broadcast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop) { - int num_ev = 0, err = 0; + int num_ev = 0, err, result = 0; struct snd_seq_client *dest_client; struct snd_seq_client_port *port; @@ -716,14 +728,18 @@ static int port_broadcast_event(struct snd_seq_client *client, err = snd_seq_deliver_single_event(NULL, event, SNDRV_SEQ_FILTER_BROADCAST, atomic, hop); - if (err < 0) - break; + if (err < 0) { + /* save first error that occurs and continue */ + if (!result) + result = err; + continue; + } num_ev++; } read_unlock(&dest_client->ports_lock); snd_seq_client_unlock(dest_client); event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ - return (err < 0) ? err : num_ev; + return (result < 0) ? result : num_ev; } /* @@ -733,7 +749,7 @@ static int port_broadcast_event(struct snd_seq_client *client, static int broadcast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop) { - int err = 0, num_ev = 0; + int err, result = 0, num_ev = 0; int dest; struct snd_seq_addr addr; @@ -752,12 +768,16 @@ static int broadcast_event(struct snd_seq_client *client, err = snd_seq_deliver_single_event(NULL, event, SNDRV_SEQ_FILTER_BROADCAST, atomic, hop); - if (err < 0) - break; + if (err < 0) { + /* save first error that occurs and continue */ + if (!result) + result = err; + continue; + } num_ev += err; } event->dest = addr; /* restore */ - return (err < 0) ? err : num_ev; + return (result < 0) ? result : num_ev; } @@ -765,7 +785,7 @@ static int broadcast_event(struct snd_seq_client *client, static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop) { - snd_printd("seq: multicast not supported yet.\n"); + pr_debug("ALSA: seq: multicast not supported yet.\n"); return 0; /* ignored */ } #endif /* SUPPORT_BROADCAST */ @@ -786,7 +806,7 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e hop++; if (hop >= SNDRV_SEQ_MAX_HOPS) { - snd_printd("too long delivery path (%d:%d->%d:%d)\n", + pr_debug("ALSA: seq: too long delivery path (%d:%d->%d:%d)\n", event->source.client, event->source.port, event->dest.client, event->dest.port); return -EMLINK; @@ -825,7 +845,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop) struct snd_seq_client *client; int result; - snd_assert(cell != NULL, return -EINVAL); + if (snd_BUG_ON(!cell)) + return -EINVAL; client = snd_seq_client_use_ptr(cell->event.source.client); if (client == NULL) { @@ -994,7 +1015,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, return -ENXIO; /* check client structures are in place */ - snd_assert(client != NULL, return -ENXIO); + if (snd_BUG_ON(!client)) + return -ENXIO; if (!client->accept_output || client->pool == NULL) return -ENXIO; @@ -1043,7 +1065,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, } else { #ifdef CONFIG_COMPAT if (client->convert32 && snd_seq_ev_is_varusr(&event)) { - void *ptr = compat_ptr(event.data.raw32.d[1]); + void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]); event.data.ext.ptr = ptr; } #endif @@ -1076,7 +1098,8 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait) unsigned int mask = 0; /* check client structures are in place */ - snd_assert(client != NULL, return -ENXIO); + if (snd_BUG_ON(!client)) + return -ENXIO; if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && client->data.user.fifo) { @@ -2185,7 +2208,7 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, if (p->cmd == cmd) return p->func(client, arg); } - snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n", + pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n", cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); return -ENOTTY; } @@ -2195,7 +2218,8 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg { struct snd_seq_client *client = file->private_data; - snd_assert(client != NULL, return -ENXIO); + if (snd_BUG_ON(!client)) + return -ENXIO; return snd_seq_do_ioctl(client, cmd, (void __user *) arg); } @@ -2216,7 +2240,8 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, struct snd_seq_client *client; va_list args; - snd_assert(! in_interrupt(), return -EBUSY); + if (snd_BUG_ON(in_interrupt())) + return -EBUSY; if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD) return -EINVAL; @@ -2265,7 +2290,8 @@ int snd_seq_delete_kernel_client(int client) { struct snd_seq_client *ptr; - snd_assert(! in_interrupt(), return -EBUSY); + if (snd_BUG_ON(in_interrupt())) + return -EBUSY; ptr = clientptr(client); if (ptr == NULL) @@ -2288,7 +2314,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev, struct snd_seq_client *cptr; int result; - snd_assert(ev != NULL, return -EINVAL); + if (snd_BUG_ON(!ev)) + return -EINVAL; if (ev->type == SNDRV_SEQ_EVENT_NONE) return 0; /* ignore this */ @@ -2354,7 +2381,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, struct snd_seq_client *cptr; int result; - snd_assert(ev != NULL, return -EINVAL); + if (snd_BUG_ON(!ev)) + return -EINVAL; /* fill in client number */ ev->queue = SNDRV_SEQ_QUEUE_DIRECT; @@ -2392,7 +2420,7 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) if (client == NULL) return -ENXIO; fs = snd_enter_user(); - result = snd_seq_do_ioctl(client, cmd, (void __user *)arg); + result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); snd_leave_user(fs); return result; } @@ -2482,9 +2510,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, } -void snd_seq_info_pool(struct snd_info_buffer *buffer, - struct snd_seq_pool *pool, char *space); - /* exported to seq_info.c */ void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -2540,6 +2565,7 @@ static const struct file_operations snd_seq_f_ops = .write = snd_seq_write, .open = snd_seq_open, .release = snd_seq_release, + .llseek = no_llseek, .poll = snd_seq_poll, .unlocked_ioctl = snd_seq_ioctl, .compat_ioctl = snd_seq_ioctl_compat, diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 9628c06e4ea..81f7c109dc4 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -21,6 +21,7 @@ /* This file included from seq.c */ #include <linux/compat.h> +#include <linux/slab.h> struct snd_seq_port_info32 { struct snd_seq_addr addr; /* client/port numbers */ @@ -48,12 +49,11 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned struct snd_seq_port_info *data; mm_segment_t fs; - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (! data) - return -ENOMEM; + data = memdup_user(data32, sizeof(*data32)); + if (IS_ERR(data)) + return PTR_ERR(data); - if (copy_from_user(data, data32, sizeof(*data32)) || - get_user(data->flags, &data32->flags) || + if (get_user(data->flags, &data32->flags) || get_user(data->time_queue, &data32->time_queue)) goto error; data->kernel = NULL; @@ -92,7 +92,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l struct snd_seq_client *client = file->private_data; void __user *argp = compat_ptr(arg); - snd_assert(client != NULL, return -ENXIO); + if (snd_BUG_ON(!client)) + return -ENXIO; switch (cmd) { case SNDRV_SEQ_IOCTL_PVERSION: diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 2f00ad28a2b..91a786a783e 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -37,6 +37,7 @@ */ #include <linux/init.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/info.h> #include <sound/seq_device.h> @@ -65,7 +66,7 @@ struct ops_list { /* operators */ struct snd_seq_dev_ops ops; - /* registred devices */ + /* registered devices */ struct list_head dev_list; /* list of devices */ int num_devices; /* number of associated devices */ int num_init_devices; /* number of initialized devices */ @@ -124,7 +125,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry, * load all registered drivers (called from seq_clientmgr.c) */ -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* avoid auto-loading during module_init() */ static int snd_seq_in_init; void snd_seq_autoload_lock(void) @@ -140,7 +141,7 @@ void snd_seq_autoload_unlock(void) void snd_seq_device_load_drivers(void) { -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES struct ops_list *ops; /* Calling request_module during module_init() @@ -167,7 +168,7 @@ void snd_seq_device_load_drivers(void) /* * register a sequencer device - * card = card info (NULL allowed) + * card = card info * device = device number (if any) * id = id of driver * result = return pointer (NULL allowed if unnecessary) @@ -187,7 +188,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, if (result) *result = NULL; - snd_assert(id != NULL, return -EINVAL); + if (snd_BUG_ON(!id)) + return -EINVAL; ops = find_driver(id, 1); if (ops == NULL) @@ -232,7 +234,8 @@ static int snd_seq_device_free(struct snd_seq_device *dev) { struct ops_list *ops; - snd_assert(dev != NULL, return -EINVAL); + if (snd_BUG_ON(!dev)) + return -EINVAL; ops = find_driver(dev->id, 0); if (ops == NULL) @@ -322,7 +325,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, return -ENOMEM; } if (ops->driver & DRIVER_LOADED) { - snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); + pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); unlock_driver(ops); snd_seq_autoload_unlock(); return -EBUSY; @@ -395,7 +398,7 @@ int snd_seq_device_unregister_driver(char *id) return -ENXIO; if (! (ops->driver & DRIVER_LOADED) || (ops->driver & DRIVER_LOCKED)) { - snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", + pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); unlock_driver(ops); return -EBUSY; @@ -410,7 +413,7 @@ int snd_seq_device_unregister_driver(char *id) ops->driver = 0; if (ops->num_init_devices > 0) - snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", + pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); mutex_unlock(&ops->reg_mutex); @@ -456,7 +459,7 @@ static int init_device(struct snd_seq_device *dev, struct ops_list *ops) if (dev->status != SNDRV_SEQ_DEVICE_FREE) return 0; /* already initialized */ if (ops->argsize != dev->argsize) { - snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", + pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } @@ -464,7 +467,7 @@ static int init_device(struct snd_seq_device *dev, struct ops_list *ops) dev->status = SNDRV_SEQ_DEVICE_REGISTERED; ops->num_init_devices++; } else { - snd_printk(KERN_ERR "init_device failed: %s: %s\n", + pr_err("ALSA: seq: init_device failed: %s: %s\n", dev->name, dev->id); } @@ -483,7 +486,7 @@ static int free_device(struct snd_seq_device *dev, struct ops_list *ops) if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) return 0; /* not registered */ if (ops->argsize != dev->argsize) { - snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", + pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } @@ -492,7 +495,7 @@ static int free_device(struct snd_seq_device *dev, struct ops_list *ops) dev->driver_data = NULL; ops->num_init_devices--; } else { - snd_printk(KERN_ERR "free_device failed: %s: %s\n", + pr_err("ALSA: seq: free_device failed: %s: %s\n", dev->name, dev->id); } @@ -556,7 +559,7 @@ static void __exit alsa_seq_device_exit(void) snd_info_free_entry(info_entry); #endif if (num_ops) - snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); + pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); } module_init(alsa_seq_device_init) @@ -566,7 +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_KMOD +#ifdef CONFIG_MODULES EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); #endif diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index f3bdc54b429..ec667f158f1 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -20,7 +20,7 @@ #include <linux/init.h> #include <linux/slab.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <sound/core.h> #include "seq_clientmgr.h" #include <sound/initval.h> @@ -46,11 +46,11 @@ The number of ports to be created can be specified via the module parameter "ports". For example, to create four ports, add the - following option in /etc/modprobe.conf: + following option in a configuration file under /etc/modprobe.d/: option snd-seq-dummy ports=4 - The modle option "duplex=1" enables duplex operation to the port. + The model option "duplex=1" enables duplex operation to the port. In duplex mode, a pair of ports are created instead of single port, and events are tunneled between pair-ports. For example, input to port A is sent to output port of another port B and vice versa. @@ -65,7 +65,7 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY)); static int ports = 1; -static int duplex; +static bool duplex; module_param(ports, int, 0444); MODULE_PARM_DESC(ports, "number of ports to be created"); @@ -198,7 +198,7 @@ register_client(void) int i; if (ports < 1) { - snd_printk(KERN_ERR "invalid number of ports %d\n", ports); + pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports); return -EINVAL; } diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 3a94ed021bd..53a403e17c5 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -34,7 +34,7 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize) f = kzalloc(sizeof(*f), GFP_KERNEL); if (f == NULL) { - snd_printd("malloc failed for snd_seq_fifo_new() \n"); + pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n"); return NULL; } @@ -65,9 +65,11 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo) { struct snd_seq_fifo *f; - snd_assert(fifo != NULL, return); + if (snd_BUG_ON(!fifo)) + return; f = *fifo; - snd_assert(f != NULL, return); + if (snd_BUG_ON(!f)) + return; *fifo = NULL; snd_seq_fifo_clear(f); @@ -116,12 +118,13 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, unsigned long flags; int err; - snd_assert(f != NULL, return -EINVAL); + if (snd_BUG_ON(!f)) + return -EINVAL; snd_use_lock_use(&f->use_lock); err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ if (err < 0) { - if (err == -ENOMEM) + if ((err == -ENOMEM) || (err == -EAGAIN)) atomic_inc(&f->overflow); snd_use_lock_free(&f->use_lock); return err; @@ -174,7 +177,8 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, unsigned long flags; wait_queue_t wait; - snd_assert(f != NULL, return -EINVAL); + if (snd_BUG_ON(!f)) + return -EINVAL; *cellp = NULL; init_waitqueue_entry(&wait, current); @@ -233,7 +237,8 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) struct snd_seq_pool *newpool, *oldpool; struct snd_seq_event_cell *cell, *next, *oldhead; - snd_assert(f != NULL && f->pool != NULL, return -EINVAL); + if (snd_BUG_ON(!f || !f->pool)) + return -EINVAL; /* allocate new pool */ newpool = snd_seq_pool_new(poolsize); diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c index 201f8106ffd..acf7769419f 100644 --- a/sound/core/seq/seq_info.c +++ b/sound/core/seq/seq_info.c @@ -20,6 +20,7 @@ */ #include <linux/init.h> +#include <linux/export.h> #include <sound/core.h> #include "seq_info.h" diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 54f921edda7..3b693e924db 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -19,6 +19,7 @@ * */ +#include <linux/export.h> #include <sound/core.h> #include "seq_lock.h" @@ -30,12 +31,12 @@ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) int max_count = 5 * HZ; if (atomic_read(lockp) < 0) { - printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); + pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); return; } while (atomic_read(lockp) > 0) { if (max_count == 0) { - snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); + pr_warn("ALSA: seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); break; } schedule_timeout_uninterruptible(1); diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 0cf6ac47731..1e206de0c2d 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -21,6 +21,7 @@ */ #include <linux/init.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <sound/core.h> @@ -86,7 +87,7 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { char buf[32]; - char __user *curptr = (char __user *)event->data.ext.ptr; + char __user *curptr = (char __force __user *)event->data.ext.ptr; while (len > 0) { int size = sizeof(buf); if (len < size) @@ -157,7 +158,7 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { if (! in_kernel) return -EINVAL; - if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len)) + if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len)) return -EFAULT; return newlen; } @@ -187,9 +188,11 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell) unsigned long flags; struct snd_seq_pool *pool; - snd_assert(cell != NULL, return); + if (snd_BUG_ON(!cell)) + return; pool = cell->pool; - snd_assert(pool != NULL, return); + if (snd_BUG_ON(!pool)) + return; spin_lock_irqsave(&pool->lock, flags); free_cell(pool, cell); @@ -233,7 +236,7 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool, init_waitqueue_entry(&wait, current); spin_lock_irqsave(&pool->lock, flags); if (pool->ptr == NULL) { /* not initialized */ - snd_printd("seq: pool is not initialized\n"); + pr_debug("ALSA: seq: pool is not initialized\n"); err = -EINVAL; goto __error; } @@ -341,7 +344,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, tmp->event = src->event; src = src->next; } else if (is_usrptr) { - if (copy_from_user(&tmp->event, (char __user *)buf, size)) { + if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) { err = -EFAULT; goto __error; } @@ -378,13 +381,14 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) struct snd_seq_event_cell *cellptr; unsigned long flags; - snd_assert(pool != NULL, return -EINVAL); + if (snd_BUG_ON(!pool)) + return -EINVAL; if (pool->ptr) /* should be atomic? */ return 0; pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); if (pool->ptr == NULL) { - snd_printd("seq: malloc for sequencer events failed\n"); + pr_debug("ALSA: seq: malloc for sequencer events failed\n"); return -ENOMEM; } @@ -414,7 +418,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) struct snd_seq_event_cell *ptr; int max_count = 5 * HZ; - snd_assert(pool != NULL, return -EINVAL); + if (snd_BUG_ON(!pool)) + return -EINVAL; /* wait for closing all threads */ spin_lock_irqsave(&pool->lock, flags); @@ -426,7 +431,7 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) while (atomic_read(&pool->counter) > 0) { if (max_count == 0) { - snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); + pr_warn("ALSA: snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); break; } schedule_timeout_uninterruptible(1); @@ -459,7 +464,7 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize) /* create pool block */ pool = kzalloc(sizeof(*pool), GFP_KERNEL); if (pool == NULL) { - snd_printd("seq: malloc failed for pool\n"); + pr_debug("ALSA: seq: malloc failed for pool\n"); return NULL; } spin_lock_init(&pool->lock); diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 63e91431a29..4a2ec779b8a 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -24,6 +24,8 @@ #include <sound/seq_kernel.h> #include <linux/poll.h> +struct snd_info_buffer; + /* container for sequencer event (internal use) */ struct snd_seq_event_cell { struct snd_seq_event event; @@ -99,5 +101,7 @@ void snd_sequencer_memory_done(void); /* polling */ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait); +void snd_seq_info_pool(struct snd_info_buffer *buffer, + struct snd_seq_pool *pool, char *space); #endif diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 99b35360c50..a1fd77af605 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -30,7 +30,7 @@ Possible options for midisynth module: #include <linux/slab.h> #include <linux/errno.h> #include <linux/string.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/rawmidi.h> @@ -116,10 +116,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i struct snd_rawmidi_runtime *runtime; int tmp; - snd_assert(substream != NULL || buf != NULL, return -EINVAL); + if (snd_BUG_ON(!substream || !buf)) + return -EINVAL; runtime = substream->runtime; if ((tmp = runtime->avail) < count) { - snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); + if (printk_ratelimit()) + pr_err("ALSA: seq_midi: MIDI output buffer overrun\n"); return -ENOMEM; } if (snd_rawmidi_kernel_write(substream, buf, count) < count) @@ -135,14 +137,15 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, struct snd_rawmidi_substream *substream; int len; - snd_assert(msynth != NULL, return -EINVAL); + if (snd_BUG_ON(!msynth)) + return -EINVAL; substream = msynth->output_rfile.output; if (substream == NULL) return -ENODEV; if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { /* invalid event */ - snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); + pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); return 0; } snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); @@ -186,7 +189,7 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) { - snd_printd("midi input open failed!!!\n"); + pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); return err; } runtime = msynth->input_rfile.input->runtime; @@ -210,7 +213,8 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri int err; struct seq_midisynth *msynth = private_data; - snd_assert(msynth->input_rfile.input != NULL, return -EINVAL); + if (snd_BUG_ON(!msynth->input_rfile.input)) + return -EINVAL; err = snd_rawmidi_kernel_release(&msynth->input_rfile); return err; } @@ -227,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) { - snd_printd("midi output open failed!!!\n"); + pr_debug("ALSA: seq_midi: midi output open failed!!!\n"); return err; } memset(¶ms, 0, sizeof(params)); params.avail_min = 1; params.buffer_size = output_buffer_size; + params.no_active_sensing = 1; if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) { snd_rawmidi_kernel_release(&msynth->output_rfile); return err; @@ -245,11 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) { struct seq_midisynth *msynth = private_data; - unsigned char buf = 0xff; /* MIDI reset */ - snd_assert(msynth->output_rfile.output != NULL, return -EINVAL); - /* sending single MIDI reset message to shut the device up */ - snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1); + if (snd_BUG_ON(!msynth->output_rfile.output)) + return -EINVAL; snd_rawmidi_drain_output(msynth->output_rfile.output); return snd_rawmidi_kernel_release(&msynth->output_rfile); } @@ -285,7 +288,8 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) int device = dev->device; unsigned int input_count = 0, output_count = 0; - snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL); + if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES)) + return -EINVAL; info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; @@ -358,13 +362,13 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) if (! port->name[0]) { if (info->name[0]) { if (ports > 1) - snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p); + snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p); else snprintf(port->name, sizeof(port->name), "%s", info->name); } else { /* last resort */ if (ports > 1) - sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p); + sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p); else sprintf(port->name, "MIDI %d-%d", card->number, device); } diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c index 07c663135c6..9b6470cdcf2 100644 --- a/sound/core/seq/seq_midi_emul.c +++ b/sound/core/seq/seq_midi_emul.c @@ -32,6 +32,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/seq_kernel.h> #include <sound/seq_midi_emul.h> @@ -88,7 +89,7 @@ snd_midi_process_event(struct snd_midi_op *ops, int dest_channel = 0; if (ev == NULL || chanset == NULL) { - snd_printd("ev or chanbase NULL (snd_midi_process_event)\n"); + pr_debug("ALSA: seq_midi_emul: ev or chanbase NULL (snd_midi_process_event)\n"); return; } if (chanset->channels == NULL) @@ -97,7 +98,7 @@ snd_midi_process_event(struct snd_midi_op *ops, if (snd_seq_ev_is_channel_type(ev)) { dest_channel = ev->data.note.channel; if (dest_channel >= chanset->max_channels) { - snd_printd("dest channel is %d, max is %d\n", + pr_debug("ALSA: seq_midi_emul: dest channel is %d, max is %d\n", dest_channel, chanset->max_channels); return; } @@ -231,7 +232,7 @@ snd_midi_process_event(struct snd_midi_op *ops, case SNDRV_SEQ_EVENT_ECHO: not_yet: default: - /*snd_printd("Unimplemented event %d\n", ev->type);*/ + /*pr_debug("ALSA: seq_midi_emul: Unimplemented event %d\n", ev->type);*/ break; } } diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index 8284f176a34..37db7ba492a 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/string.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/seq_kernel.h> #include <sound/seq_midi_event.h> @@ -504,10 +505,10 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf, if (dev->nostat && count < 12) return -ENOMEM; cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); - bytes[0] = ev->data.control.param & 0x007f; - bytes[1] = (ev->data.control.param & 0x3f80) >> 7; - bytes[2] = ev->data.control.value & 0x007f; - bytes[3] = (ev->data.control.value & 0x3f80) >> 7; + bytes[0] = (ev->data.control.param & 0x3f80) >> 7; + bytes[1] = ev->data.control.param & 0x007f; + bytes[2] = (ev->data.control.value & 0x3f80) >> 7; + bytes[3] = ev->data.control.value & 0x007f; if (cmd != dev->lastcmd && !dev->nostat) { if (count < 9) return -ENOMEM; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 1c32a53d6bd..794a341bf0e 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -22,6 +22,7 @@ #include <sound/core.h> #include <linux/slab.h> +#include <linux/module.h> #include "seq_system.h" #include "seq_ports.h" #include "seq_clientmgr.h" @@ -130,17 +131,18 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int num = -1; /* sanity check */ - snd_assert(client, return NULL); + if (snd_BUG_ON(!client)) + return NULL; if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { - snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); + pr_warn("ALSA: seq: too many ports for client %d\n", client->number); return NULL; } /* create a new port */ new_port = kzalloc(sizeof(*new_port), GFP_KERNEL); if (! new_port) { - snd_printd("malloc failed for registering client port\n"); + pr_debug("ALSA: seq: malloc failed for registering client port\n"); return NULL; /* failure, out of memory */ } /* init port data */ @@ -268,8 +270,8 @@ static int port_delete(struct snd_seq_client *client, if (port->private_free) port->private_free(port->private_data); - snd_assert(port->c_src.count == 0,); - snd_assert(port->c_dest.count == 0,); + snd_BUG_ON(port->c_src.count != 0); + snd_BUG_ON(port->c_dest.count != 0); kfree(port); return 0; @@ -336,7 +338,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client) int snd_seq_set_port_info(struct snd_seq_client_port * port, struct snd_seq_port_info * info) { - snd_assert(port && info, return -EINVAL); + if (snd_BUG_ON(!port || !info)) + return -EINVAL; /* set port name */ if (info->name[0]) @@ -365,7 +368,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port, int snd_seq_get_port_info(struct snd_seq_client_port * port, struct snd_seq_port_info * info) { - snd_assert(port && info, return -EINVAL); + if (snd_BUG_ON(!port || !info)) + return -EINVAL; /* get port name */ strlcpy(info->name, port->name, sizeof(info->name)); @@ -409,7 +413,7 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port, * initialization or termination of devices (see seq_midi.c). * * If callback_all option is set, the callback function is invoked - * at each connnection/disconnection. + * at each connection/disconnection. */ static int subscribe_port(struct snd_seq_client *client, diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 85969db576c..021b02bc933 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -60,7 +60,7 @@ struct snd_seq_prioq *snd_seq_prioq_new(void) f = kzalloc(sizeof(*f), GFP_KERNEL); if (f == NULL) { - snd_printd("oops: malloc failed for snd_seq_prioq_new()\n"); + pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n"); return NULL; } @@ -79,7 +79,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo) *fifo = NULL; if (f == NULL) { - snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n"); + pr_debug("ALSA: seq: snd_seq_prioq_delete() called with NULL prioq\n"); return; } @@ -153,8 +153,8 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, int count; int prior; - snd_assert(f, return -EINVAL); - snd_assert(cell, return -EINVAL); + if (snd_BUG_ON(!f || !cell)) + return -EINVAL; /* check flags */ prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); @@ -197,7 +197,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, cur = cur->next; if (! --count) { spin_unlock_irqrestore(&f->lock, flags); - snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n"); + pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n"); return -EINVAL; } } @@ -223,7 +223,7 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f) unsigned long flags; if (f == NULL) { - snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n"); return NULL; } spin_lock_irqsave(&f->lock, flags); @@ -248,7 +248,7 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f) int snd_seq_prioq_avail(struct snd_seq_prioq * f) { if (f == NULL) { - snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n"); return 0; } return f->cells; @@ -259,7 +259,7 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f) struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f) { if (f == NULL) { - snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n"); return NULL; } return f->head; @@ -321,7 +321,8 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp) freeprev = cell; } else { #if 0 - printk("type = %i, source = %i, dest = %i, client = %i\n", + pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, " + "client = %i\n", cell->event.type, cell->event.source.client, cell->event.dest.client, diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 4a48c6ee8ee..aad4878cee5 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -112,7 +112,7 @@ static struct snd_seq_queue *queue_new(int owner, int locked) q = kzalloc(sizeof(*q), GFP_KERNEL); if (q == NULL) { - snd_printd("malloc failed for snd_seq_queue_new()\n"); + pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n"); return NULL; } @@ -315,7 +315,8 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop) int dest, err; struct snd_seq_queue *q; - snd_assert(cell != NULL, return -EINVAL); + if (snd_BUG_ON(!cell)) + return -EINVAL; dest = cell->event.queue; /* destination queue */ q = queueptr(dest); if (q == NULL) @@ -466,13 +467,11 @@ int snd_seq_queue_timer_open(int queueid) int snd_seq_queue_timer_close(int queueid) { struct snd_seq_queue *queue; - struct snd_seq_timer *tmr; int result = 0; queue = queueptr(queueid); if (queue == NULL) return -EINVAL; - tmr = queue->timer; snd_seq_timer_close(queue); queuefree(queue); return result; @@ -734,7 +733,8 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop) { struct snd_seq_queue *q; - snd_assert(ev != NULL, return -EINVAL); + if (snd_BUG_ON(!ev)) + return -EINVAL; q = queueptr(ev->data.queue.queue); if (q == NULL) diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 77884e62b64..8ce1d0b40dc 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -20,6 +20,8 @@ */ #include <linux/init.h> +#include <linux/export.h> +#include <linux/slab.h> #include <sound/core.h> #include "seq_system.h" #include "seq_timer.h" diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index d8fcd62e400..e73605393ee 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -33,22 +33,21 @@ #define SKEW_BASE 0x10000 /* 16bit shift */ -static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick, - int tempo, int ppq) +static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr) { - if (tempo < 1000000) - tick->resolution = (tempo * 1000) / ppq; + if (tmr->tempo < 1000000) + tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq; else { /* might overflow.. */ unsigned int s; - s = tempo % ppq; - s = (s * 1000) / ppq; - tick->resolution = (tempo / ppq) * 1000; - tick->resolution += s; + s = tmr->tempo % tmr->ppq; + s = (s * 1000) / tmr->ppq; + tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000; + tmr->tick.resolution += s; } - if (tick->resolution <= 0) - tick->resolution = 1; - snd_seq_timer_update_tick(tick, 0); + if (tmr->tick.resolution <= 0) + tmr->tick.resolution = 1; + snd_seq_timer_update_tick(&tmr->tick, 0); } /* create new timer (constructor) */ @@ -58,7 +57,7 @@ struct snd_seq_timer *snd_seq_timer_new(void) tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); if (tmr == NULL) { - snd_printd("malloc failed for snd_seq_timer_new() \n"); + pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n"); return NULL; } spin_lock_init(&tmr->lock); @@ -79,7 +78,7 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr) *tmr = NULL; if (t == NULL) { - snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n"); + pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n"); return; } t->running = 0; @@ -96,7 +95,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) /* setup defaults */ tmr->ppq = 96; /* 96 PPQ */ tmr->tempo = 500000; /* 120 BPM */ - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); tmr->running = 0; tmr->type = SNDRV_SEQ_TIMER_ALSA; @@ -173,13 +172,14 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) { unsigned long flags; - snd_assert(tmr, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; if (tempo <= 0) return -EINVAL; spin_lock_irqsave(&tmr->lock, flags); if ((unsigned int)tempo != tmr->tempo) { tmr->tempo = tempo; - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); } spin_unlock_irqrestore(&tmr->lock, flags); return 0; @@ -190,7 +190,8 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq) { unsigned long flags; - snd_assert(tmr, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; if (ppq <= 0) return -EINVAL; spin_lock_irqsave(&tmr->lock, flags); @@ -198,12 +199,12 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq) /* refuse to change ppq on running timers */ /* because it will upset the song position (ticks) */ spin_unlock_irqrestore(&tmr->lock, flags); - snd_printd("seq: cannot change ppq of a running timer\n"); + pr_debug("ALSA: seq: cannot change ppq of a running timer\n"); return -EBUSY; } tmr->ppq = ppq; - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -214,7 +215,8 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, { unsigned long flags; - snd_assert(tmr, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; spin_lock_irqsave(&tmr->lock, flags); tmr->tick.cur_tick = position; @@ -229,7 +231,8 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, { unsigned long flags; - snd_assert(tmr, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; snd_seq_sanity_real_time(&position); spin_lock_irqsave(&tmr->lock, flags); @@ -244,11 +247,12 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, { unsigned long flags; - snd_assert(tmr, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; /* FIXME */ if (base != SKEW_BASE) { - snd_printd("invalid skew base 0x%x\n", base); + pr_debug("ALSA: seq: invalid skew base 0x%x\n", base); return -EINVAL; } spin_lock_irqsave(&tmr->lock, flags); @@ -265,7 +269,8 @@ int snd_seq_timer_open(struct snd_seq_queue *q) int err; tmr = q->timer; - snd_assert(tmr != NULL, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; if (tmr->timeri) return -EBUSY; sprintf(str, "sequencer queue %i", q->queue); @@ -285,10 +290,10 @@ int snd_seq_timer_open(struct snd_seq_queue *q) tid.device = SNDRV_TIMER_GLOBAL_SYSTEM; err = snd_timer_open(&t, str, &tid, q->queue); } - if (err < 0) { - snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err); - return err; - } + } + if (err < 0) { + pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err); + return err; } t->callback = snd_seq_timer_interrupt; t->callback_data = q; @@ -302,7 +307,8 @@ int snd_seq_timer_close(struct snd_seq_queue *q) struct snd_seq_timer *tmr; tmr = q->timer; - snd_assert(tmr != NULL, return -EINVAL); + if (snd_BUG_ON(!tmr)) + return -EINVAL; if (tmr->timeri) { snd_timer_stop(tmr->timeri); snd_timer_close(tmr->timeri); @@ -328,7 +334,8 @@ static int initialize_timer(struct snd_seq_timer *tmr) unsigned long freq; t = tmr->timeri->timer; - snd_assert(t, return -EINVAL); + if (snd_BUG_ON(!t)) + return -EINVAL; freq = tmr->preferred_resolution; if (!freq) diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 86e7739269c..56e0f4cd3f8 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -37,6 +37,7 @@ #include <linux/init.h> #include <linux/wait.h> +#include <linux/module.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/rawmidi.h> @@ -445,7 +446,7 @@ static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi) /* should check presence of port more strictly.. */ break; default: - snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode); + pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode); return -EINVAL; } return 0; diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index cefd228cd2a..0a418503ec4 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> @@ -38,17 +39,20 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) if (! sgbuf) return -EINVAL; + if (dmab->area) + vunmap(dmab->area); + dmab->area = NULL; + tmpb.dev.type = SNDRV_DMA_TYPE_DEV; tmpb.dev.dev = sgbuf->dev; for (i = 0; i < sgbuf->pages; i++) { + if (!(sgbuf->table[i].addr & ~PAGE_MASK)) + continue; /* continuous pages */ tmpb.area = sgbuf->table[i].buf; - tmpb.addr = sgbuf->table[i].addr; - tmpb.bytes = PAGE_SIZE; + tmpb.addr = sgbuf->table[i].addr & PAGE_MASK; + tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT; snd_dma_free_pages(&tmpb); } - if (dmab->area) - vunmap(dmab->area); - dmab->area = NULL; kfree(sgbuf->table); kfree(sgbuf->page_table); @@ -58,13 +62,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) return 0; } +#define MAX_ALLOC_PAGES 32 + void *snd_malloc_sgbuf_pages(struct device *device, size_t size, struct snd_dma_buffer *dmab, size_t *res_size) { struct snd_sg_buf *sgbuf; - unsigned int i, pages; + unsigned int i, pages, chunk, maxpages; struct snd_dma_buffer tmpb; + struct snd_sg_page *table; + struct page **pgtable; dmab->area = NULL; dmab->addr = 0; @@ -74,34 +82,84 @@ void *snd_malloc_sgbuf_pages(struct device *device, sgbuf->dev = device; pages = snd_sgbuf_aligned_pages(size); sgbuf->tblsize = sgbuf_align_table(pages); - sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL); - if (! sgbuf->table) + table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL); + if (!table) goto _failed; - sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL); - if (! sgbuf->page_table) + sgbuf->table = table; + pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL); + if (!pgtable) goto _failed; + sgbuf->page_table = pgtable; - /* allocate each page */ - for (i = 0; i < pages; i++) { - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) { - if (res_size == NULL) + /* allocate pages */ + maxpages = MAX_ALLOC_PAGES; + while (pages > 0) { + chunk = pages; + /* don't be too eager to take a huge chunk */ + if (chunk > maxpages) + chunk = maxpages; + chunk <<= PAGE_SHIFT; + if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device, + chunk, &tmpb) < 0) { + if (!sgbuf->pages) + goto _failed; + if (!res_size) goto _failed; - *res_size = size = sgbuf->pages * PAGE_SIZE; + size = sgbuf->pages * PAGE_SIZE; break; } - sgbuf->table[i].buf = tmpb.area; - sgbuf->table[i].addr = tmpb.addr; - sgbuf->page_table[i] = virt_to_page(tmpb.area); - sgbuf->pages++; + chunk = tmpb.bytes >> PAGE_SHIFT; + for (i = 0; i < chunk; i++) { + table->buf = tmpb.area; + table->addr = tmpb.addr; + if (!i) + table->addr |= chunk; /* mark head */ + table++; + *pgtable++ = virt_to_page(tmpb.area); + tmpb.area += PAGE_SIZE; + tmpb.addr += PAGE_SIZE; + } + sgbuf->pages += chunk; + pages -= chunk; + if (chunk < maxpages) + maxpages = chunk; } sgbuf->size = size; dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL); if (! dmab->area) goto _failed; + if (res_size) + *res_size = sgbuf->size; return dmab->area; _failed: 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 812f91b3de5..38ad1a0dd3f 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -23,18 +23,15 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/device.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #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> #include <linux/mutex.h> -#define SNDRV_OS_MINORS 256 - static int major = CONFIG_SND_MAJOR; int snd_major; EXPORT_SYMBOL(snd_major); @@ -60,14 +57,14 @@ EXPORT_SYMBOL(snd_ecards_limit); static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; static DEFINE_MUTEX(sound_mutex); -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /** * snd_request_card - try to load the card module * @card: the card number * * Tries to load the module "snd-card-X" for the given card number - * via KMOD. Returns immediately if already loaded. + * via request_module. Returns immediately if already loaded. */ void snd_request_card(int card) { @@ -92,7 +89,7 @@ static void snd_request_other(int minor) request_module(str); } -#endif /* request_module support */ +#endif /* modular kernel */ /** * snd_lookup_minor_data - get user data of a registered device @@ -101,6 +98,13 @@ static void snd_request_other(int minor) * * Checks that a minor device with the specified type is registered, and returns * its user data pointer. + * + * This function increments the reference counter of the card instance + * if an associated instance with the given minor number and type is found. + * The caller must call snd_card_unref() appropriately later. + * + * Return: The user data pointer if the specified device is found. %NULL + * otherwise. */ void *snd_lookup_minor_data(unsigned int minor, int type) { @@ -111,9 +115,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type) return NULL; mutex_lock(&sound_mutex); mreg = snd_minors[minor]; - if (mreg && mreg->type == type) + if (mreg && mreg->type == type) { private_data = mreg->private_data; - else + if (private_data && mreg->card_ptr) + get_device(&mreg->card_ptr->card_dev); + } else private_data = NULL; mutex_unlock(&sound_mutex); return private_data; @@ -121,63 +127,81 @@ void *snd_lookup_minor_data(unsigned int minor, int type) EXPORT_SYMBOL(snd_lookup_minor_data); +#ifdef CONFIG_MODULES +static struct snd_minor *autoload_device(unsigned int minor) +{ + int dev; + mutex_unlock(&sound_mutex); /* release lock temporarily */ + dev = SNDRV_MINOR_DEVICE(minor); + if (dev == SNDRV_MINOR_CONTROL) { + /* /dev/aloadC? */ + int card = SNDRV_MINOR_CARD(minor); + if (snd_cards[card] == NULL) + snd_request_card(card); + } else if (dev == SNDRV_MINOR_GLOBAL) { + /* /dev/aloadSEQ */ + snd_request_other(minor); + } + mutex_lock(&sound_mutex); /* reacuire lock */ + return snd_minors[minor]; +} +#else /* !CONFIG_MODULES */ +#define autoload_device(minor) NULL +#endif /* CONFIG_MODULES */ + static int snd_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct snd_minor *mptr = NULL; - const struct file_operations *old_fops; + const struct file_operations *new_fops; int err = 0; if (minor >= ARRAY_SIZE(snd_minors)) return -ENODEV; + mutex_lock(&sound_mutex); mptr = snd_minors[minor]; if (mptr == NULL) { -#ifdef CONFIG_KMOD - int dev = SNDRV_MINOR_DEVICE(minor); - if (dev == SNDRV_MINOR_CONTROL) { - /* /dev/aloadC? */ - int card = SNDRV_MINOR_CARD(minor); - if (snd_cards[card] == NULL) - snd_request_card(card); - } else if (dev == SNDRV_MINOR_GLOBAL) { - /* /dev/aloadSEQ */ - snd_request_other(minor); - } -#ifndef CONFIG_SND_DYNAMIC_MINORS - /* /dev/snd/{controlC?,seq} */ - mptr = snd_minors[minor]; - if (mptr == NULL) -#endif -#endif + mptr = autoload_device(minor); + if (!mptr) { + mutex_unlock(&sound_mutex); return -ENODEV; + } } - old_fops = file->f_op; - file->f_op = fops_get(mptr->f_ops); + new_fops = fops_get(mptr->f_ops); + mutex_unlock(&sound_mutex); + if (!new_fops) + return -ENODEV; + replace_fops(file, new_fops); + if (file->f_op->open) err = file->f_op->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); return err; } static const struct file_operations snd_fops = { .owner = THIS_MODULE, - .open = snd_open + .open = snd_open, + .llseek = noop_llseek, }; #ifdef CONFIG_SND_DYNAMIC_MINORS -static int snd_find_free_minor(void) +static int snd_find_free_minor(int type) { int minor; + /* static minors for module auto loading */ + if (type == SNDRV_DEVICE_TYPE_SEQUENCER) + return SNDRV_MINOR_SEQUENCER; + if (type == SNDRV_DEVICE_TYPE_TIMER) + return SNDRV_MINOR_TIMER; + for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { - /* skip minors still used statically for autoloading devices */ - if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL || - minor == SNDRV_MINOR_SEQUENCER) + /* skip static minors still used for module auto loading */ + if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) + continue; + if (minor == SNDRV_MINOR_SEQUENCER || + minor == SNDRV_MINOR_TIMER) continue; if (!snd_minors[minor]) return minor; @@ -195,20 +219,24 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) minor = type; break; case SNDRV_DEVICE_TYPE_CONTROL: - snd_assert(card != NULL, return -EINVAL); + if (snd_BUG_ON(!card)) + return -EINVAL; minor = SNDRV_MINOR(card->number, type); break; case SNDRV_DEVICE_TYPE_HWDEP: case SNDRV_DEVICE_TYPE_RAWMIDI: case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: case SNDRV_DEVICE_TYPE_PCM_CAPTURE: - snd_assert(card != NULL, return -EINVAL); + case SNDRV_DEVICE_TYPE_COMPRESS: + if (snd_BUG_ON(!card)) + return -EINVAL; minor = SNDRV_MINOR(card->number, type + dev); break; default: return -EINVAL; } - snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) + return -EINVAL; return minor; } #endif @@ -226,7 +254,7 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, @@ -236,7 +264,8 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, int minor; struct snd_minor *preg; - snd_assert(name, return -EINVAL); + if (snd_BUG_ON(!name)) + return -EINVAL; preg = kmalloc(sizeof *preg, GFP_KERNEL); if (preg == NULL) return -ENOMEM; @@ -245,9 +274,10 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, preg->device = dev; preg->f_ops = f_ops; preg->private_data = private_data; + preg->card_ptr = card; mutex_lock(&sound_mutex); #ifdef CONFIG_SND_DYNAMIC_MINORS - minor = snd_find_free_minor(); + minor = snd_find_free_minor(type); #else minor = snd_kernel_minor(type, card, dev); if (minor >= 0 && snd_minors[minor]) @@ -260,7 +290,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, } snd_minors[minor] = preg; preg->dev = device_create(sound_class, device, MKDEV(major, minor), - "%s", name); + private_data, "%s", name); if (IS_ERR(preg->dev)) { snd_minors[minor] = NULL; mutex_unlock(&sound_mutex); @@ -269,9 +299,6 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, return minor; } - if (preg->dev) - dev_set_drvdata(preg->dev, private_data); - mutex_unlock(&sound_mutex); return 0; } @@ -305,7 +332,7 @@ static int find_snd_minor(int type, struct snd_card *card, int dev) * Unregisters the device file already registered via * snd_register_device(). * - * Returns zero if sucecessful, or a negative error code on failure + * Return: Zero if successful, or a negative error code on failure. */ int snd_unregister_device(int type, struct snd_card *card, int dev) { @@ -328,22 +355,25 @@ int snd_unregister_device(int type, struct snd_card *card, int dev) EXPORT_SYMBOL(snd_unregister_device); -int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev, - struct device_attribute *attr) +/* get the assigned device to the given type and device number; + * the caller needs to release it via put_device() after using it + */ +struct device *snd_get_device(int type, struct snd_card *card, int dev) { - int minor, ret = -EINVAL; - struct device *d; + int minor; + struct device *d = NULL; mutex_lock(&sound_mutex); minor = find_snd_minor(type, card, dev); - if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL) - ret = device_create_file(d, attr); + if (minor >= 0) { + d = snd_minors[minor]->dev; + if (d) + get_device(d); + } mutex_unlock(&sound_mutex); - return ret; - + return d; } - -EXPORT_SYMBOL(snd_add_device_sysfs_file); +EXPORT_SYMBOL(snd_get_device); #ifdef CONFIG_PROC_FS /* @@ -431,7 +461,7 @@ static int __init alsa_sound_init(void) snd_major = major; snd_ecards_limit = cards_limit; if (register_chrdev(major, "alsa", &snd_fops)) { - snd_printk(KERN_ERR "unable to register native major device number %d\n", major); + pr_err("ALSA core: unable to register native major device number %d\n", major); return -EIO; } if (snd_info_init() < 0) { @@ -440,7 +470,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"); + pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); #endif return 0; } @@ -452,5 +482,5 @@ static void __exit alsa_sound_exit(void) unregister_chrdev(major, "alsa"); } -module_init(alsa_sound_init) -module_exit(alsa_sound_exit) +subsys_initcall(alsa_sound_init); +module_exit(alsa_sound_exit); diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 7be51546eb9..573a65eb2b7 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -21,11 +21,12 @@ #ifdef CONFIG_SND_OSSEMUL -#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE)) +#if !IS_ENABLED(CONFIG_SOUND) #error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel." #endif #include <linux/init.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/time.h> #include <sound/core.h> @@ -34,11 +35,14 @@ #include <linux/sound.h> #include <linux/mutex.h> -#define SNDRV_OSS_MINORS 128 +#define SNDRV_OSS_MINORS 256 static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS]; static DEFINE_MUTEX(sound_oss_mutex); +/* NOTE: This function increments the refcount of the associated card like + * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately + */ void *snd_lookup_oss_minor_data(unsigned int minor, int type) { struct snd_minor *mreg; @@ -48,9 +52,11 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) return NULL; mutex_lock(&sound_oss_mutex); mreg = snd_oss_minors[minor]; - if (mreg && mreg->type == type) + if (mreg && mreg->type == type) { private_data = mreg->private_data; - else + if (private_data && mreg->card_ptr) + get_device(&mreg->card_ptr->card_dev); + } else private_data = NULL; mutex_unlock(&sound_oss_mutex); return private_data; @@ -64,7 +70,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) switch (type) { case SNDRV_OSS_DEVICE_TYPE_MIXER: - snd_assert(card != NULL && dev <= 1, return -EINVAL); + if (snd_BUG_ON(!card || dev < 0 || dev > 1)) + return -EINVAL; minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER)); break; case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: @@ -74,11 +81,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) minor = SNDRV_MINOR_OSS_MUSIC; break; case SNDRV_OSS_DEVICE_TYPE_PCM: - snd_assert(card != NULL && dev <= 1, return -EINVAL); + if (snd_BUG_ON(!card || dev < 0 || dev > 1)) + return -EINVAL; minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM)); break; case SNDRV_OSS_DEVICE_TYPE_MIDI: - snd_assert(card != NULL && dev <= 1, return -EINVAL); + if (snd_BUG_ON(!card || dev < 0 || dev > 1)) + return -EINVAL; minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI)); break; case SNDRV_OSS_DEVICE_TYPE_DMFM: @@ -90,13 +99,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) default: return -EINVAL; } - snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL); + if (minor < 0 || minor >= SNDRV_OSS_MINORS) + return -EINVAL; return minor; } int snd_register_oss_device(int type, struct snd_card *card, int dev, - const struct file_operations *f_ops, void *private_data, - const char *name) + const struct file_operations *f_ops, void *private_data) { int minor = snd_oss_kernel_minor(type, card, dev); int minor_unit; @@ -106,7 +115,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, int register1 = -1, register2 = -1; struct device *carddev = snd_card_get_device_link(card); - if (card && card->number >= 8) + if (card && card->number >= SNDRV_MINOR_OSS_DEVICES) return 0; /* ignore silently */ if (minor < 0) return minor; @@ -118,6 +127,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, preg->device = dev; preg->f_ops = f_ops; preg->private_data = private_data; + preg->card_ptr = card; mutex_lock(&sound_oss_mutex); snd_oss_minors[minor] = preg; minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); @@ -165,7 +175,7 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev) int track2 = -1; struct snd_minor *mptr; - if (card && card->number >= 8) + if (card && card->number >= SNDRV_MINOR_OSS_DEVICES) return 0; if (minor < 0) return minor; diff --git a/sound/core/timer.c b/sound/core/timer.c index 9d8184a2c2d..777a45e08e5 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -24,7 +24,8 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/mutex.h> -#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/module.h> #include <linux/string.h> #include <sound/core.h> #include <sound/timer.h> @@ -34,9 +35,9 @@ #include <sound/initval.h> #include <linux/kmod.h> -#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE) -#define DEFAULT_TIMER_LIMIT 3 -#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE) +#if IS_ENABLED(CONFIG_SND_HRTIMER) +#define DEFAULT_TIMER_LIMIT 4 +#elif IS_ENABLED(CONFIG_SND_RTCTIMER) #define DEFAULT_TIMER_LIMIT 2 #else #define DEFAULT_TIMER_LIMIT 1 @@ -52,6 +53,9 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); module_param(timer_tstamp_monotonic, int, 0444); MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); +MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); +MODULE_ALIAS("devname:snd/timer"); + struct snd_timer_user { struct snd_timer_instance *timeri; int tread; /* enhanced read with timestamps and events */ @@ -146,7 +150,7 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) return NULL; } -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES static void snd_timer_request(struct snd_timer_id *tid) { @@ -183,9 +187,8 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave) list_for_each_entry(master, &timer->open_list_head, open_list) { if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { - list_del(&slave->open_list); - list_add_tail(&slave->open_list, - &master->slave_list_head); + list_move_tail(&slave->open_list, + &master->slave_list_head); spin_lock_irq(&slave_active_lock); slave->master = master; slave->timer = master->timer; @@ -237,7 +240,8 @@ int snd_timer_open(struct snd_timer_instance **ti, /* open a slave instance */ if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { - snd_printd("invalid slave class %i\n", tid->dev_sclass); + pr_debug("ALSA: timer: invalid slave class %i\n", + tid->dev_sclass); return -EINVAL; } mutex_lock(®ister_mutex); @@ -259,8 +263,8 @@ int snd_timer_open(struct snd_timer_instance **ti, /* open a master instance */ mutex_lock(®ister_mutex); timer = snd_timer_find(tid); -#ifdef CONFIG_KMOD - if (timer == NULL) { +#ifdef CONFIG_MODULES + if (!timer) { mutex_unlock(®ister_mutex); snd_timer_request(tid); mutex_lock(®ister_mutex); @@ -306,7 +310,8 @@ int snd_timer_close(struct snd_timer_instance *timeri) struct snd_timer *timer = NULL; struct snd_timer_instance *slave, *tmp; - snd_assert(timeri != NULL, return -ENXIO); + if (snd_BUG_ON(!timeri)) + return -ENXIO; /* force to stop the timer */ snd_timer_stop(timeri); @@ -325,6 +330,8 @@ int snd_timer_close(struct snd_timer_instance *timeri) mutex_unlock(®ister_mutex); } else { timer = timeri->timer; + if (snd_BUG_ON(!timer)) + goto out; /* wait, until the active callback is finished */ spin_lock_irq(&timer->lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { @@ -350,6 +357,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) } mutex_unlock(®ister_mutex); } + out: if (timeri->private_free) timeri->private_free(timeri); kfree(timeri->owner); @@ -382,16 +390,17 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) struct timespec tstamp; if (timer_tstamp_monotonic) - do_posix_clock_monotonic_gettime(&tstamp); + ktime_get_ts(&tstamp); else getnstimeofday(&tstamp); - snd_assert(event >= SNDRV_TIMER_EVENT_START && - event <= SNDRV_TIMER_EVENT_PAUSE, return); + if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || + event > SNDRV_TIMER_EVENT_PAUSE)) + return; if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE) resolution = snd_timer_resolution(ti); if (ti->ccallback) - ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution); + ti->ccallback(ti, event, &tstamp, resolution); if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) return; timer = ti->timer; @@ -409,8 +418,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, unsigned long sticks) { - list_del(&timeri->active_list); - list_add_tail(&timeri->active_list, &timer->active_list_head); + list_move_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) goto __start_now; @@ -474,7 +482,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri, struct snd_timer *timer; unsigned long flags; - snd_assert(timeri != NULL, return -ENXIO); + if (snd_BUG_ON(!timeri)) + return -ENXIO; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { if (!keep_flag) { @@ -527,6 +536,8 @@ int snd_timer_stop(struct snd_timer_instance *timeri) if (err < 0) return err; timer = timeri->timer; + if (!timer) + return -EINVAL; spin_lock_irqsave(&timer->lock, flags); timeri->cticks = timeri->ticks; timeri->pticks = 0; @@ -740,7 +751,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) spin_unlock_irqrestore(&timer->lock, flags); if (use_tasklet) - tasklet_hi_schedule(&timer->task_queue); + tasklet_schedule(&timer->task_queue); } /* @@ -758,12 +769,13 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, .dev_disconnect = snd_timer_dev_disconnect, }; - snd_assert(tid != NULL, return -EINVAL); - snd_assert(rtimer != NULL, return -EINVAL); - *rtimer = NULL; + if (snd_BUG_ON(!tid)) + return -EINVAL; + if (rtimer) + *rtimer = NULL; timer = kzalloc(sizeof(*timer), GFP_KERNEL); if (timer == NULL) { - snd_printk(KERN_ERR "timer: cannot allocate\n"); + pr_err("ALSA: timer: cannot allocate\n"); return -ENOMEM; } timer->tmr_class = tid->dev_class; @@ -788,19 +800,21 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, return err; } } - *rtimer = timer; + if (rtimer) + *rtimer = timer; return 0; } static int snd_timer_free(struct snd_timer *timer) { - snd_assert(timer != NULL, return -ENXIO); + if (!timer) + return 0; mutex_lock(®ister_mutex); if (! list_empty(&timer->open_list_head)) { struct list_head *p, *n; struct snd_timer_instance *ti; - snd_printk(KERN_WARNING "timer %p is busy?\n", timer); + pr_warn("ALSA: timer %p is busy?\n", timer); list_for_each_safe(p, n, &timer->open_list_head) { list_del_init(p); ti = list_entry(p, struct snd_timer_instance, open_list); @@ -827,8 +841,8 @@ static int snd_timer_dev_register(struct snd_device *dev) struct snd_timer *timer = dev->device_data; struct snd_timer *timer1; - snd_assert(timer != NULL && timer->hw.start != NULL && - timer->hw.stop != NULL, return -ENXIO); + if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) + return -ENXIO; if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && !timer->hw.resolution && timer->hw.c_resolution == NULL) return -EINVAL; @@ -879,8 +893,9 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) return; - snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && - event <= SNDRV_TIMER_EVENT_MRESUME, return); + if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || + event > SNDRV_TIMER_EVENT_MRESUME)) + return; spin_lock_irqsave(&timer->lock, flags); if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE || @@ -1153,6 +1168,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, { struct snd_timer_user *tu = timeri->callback_data; struct snd_timer_tread r1; + unsigned long flags; if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) @@ -1162,9 +1178,9 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, r1.event = event; r1.tstamp = *tstamp; r1.val = resolution; - spin_lock(&tu->qlock); + spin_lock_irqsave(&tu->qlock, flags); snd_timer_user_append_to_tqueue(tu, &r1); - spin_unlock(&tu->qlock); + spin_unlock_irqrestore(&tu->qlock, flags); kill_fasync(&tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } @@ -1187,7 +1203,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, } if (tu->last_resolution != resolution || ticks > 0) { if (timer_tstamp_monotonic) - do_posix_clock_monotonic_gettime(&tstamp); + ktime_get_ts(&tstamp); else getnstimeofday(&tstamp); } @@ -1230,6 +1246,11 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, static int snd_timer_user_open(struct inode *inode, struct file *file) { struct snd_timer_user *tu; + int err; + + err = nonseekable_open(inode, file); + if (err < 0) + return err; tu = kzalloc(sizeof(*tu), GFP_KERNEL); if (tu == NULL) @@ -1256,7 +1277,6 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) if (file->private_data) { tu = file->private_data; file->private_data = NULL; - fasync_helper(-1, file, 0, &tu->fasync); if (tu->timeri) snd_timer_close(tu->timeri); kfree(tu->queue); @@ -1389,13 +1409,10 @@ static int snd_timer_user_ginfo(struct file *file, struct list_head *p; int err = 0; - ginfo = kmalloc(sizeof(*ginfo), GFP_KERNEL); - if (! ginfo) - return -ENOMEM; - if (copy_from_user(ginfo, _ginfo, sizeof(*ginfo))) { - kfree(ginfo); - return -EFAULT; - } + ginfo = memdup_user(_ginfo, sizeof(*ginfo)); + if (IS_ERR(ginfo)) + return PTR_ERR(ginfo); + tid = ginfo->tid; memset(ginfo, 0, sizeof(*ginfo)); ginfo->tid = tid; @@ -1819,13 +1836,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, static int snd_timer_user_fasync(int fd, struct file * file, int on) { struct snd_timer_user *tu; - int err; tu = file->private_data; - err = fasync_helper(fd, file, on, &tu->fasync); - if (err < 0) - return err; - return 0; + return fasync_helper(fd, file, on, &tu->fasync); } static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, @@ -1922,6 +1935,7 @@ static const struct file_operations snd_timer_f_ops = .read = snd_timer_user_read, .open = snd_timer_user_open, .release = snd_timer_user_release, + .llseek = no_llseek, .poll = snd_timer_user_poll, .unlocked_ioctl = snd_timer_user_ioctl, .compat_ioctl = snd_timer_user_ioctl_compat, @@ -1942,12 +1956,10 @@ static int __init alsa_timer_init(void) #endif if ((err = snd_timer_register_system()) < 0) - snd_printk(KERN_ERR "unable to register system timer (%i)\n", - err); + pr_err("ALSA: unable to register system timer (%i)\n", err); if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, &snd_timer_f_ops, NULL, "timer")) < 0) - snd_printk(KERN_ERR "unable to register timer device (%i)\n", - err); + pr_err("ALSA: unable to register timer device (%i)\n", err); snd_timer_proc_init(); return 0; } diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index 5512f5373c5..e05802ae6e1 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -40,9 +40,11 @@ static int snd_timer_user_info_compat(struct file *file, struct snd_timer *t; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (snd_BUG_ON(!tu->timeri)) + return -ENXIO; t = tu->timeri->timer; - snd_assert(t != NULL, return -ENXIO); + if (snd_BUG_ON(!t)) + return -ENXIO; memset(&info, 0, sizeof(info)); info.card = t->card ? t->card->number : -1; if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) @@ -71,7 +73,8 @@ static int snd_timer_user_status_compat(struct file *file, struct snd_timer_status status; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (snd_BUG_ON(!tu->timeri)) + return -ENXIO; memset(&status, 0, sizeof(status)); status.tstamp = tu->tstamp; status.resolution = snd_timer_resolution(tu->timeri); diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c new file mode 100644 index 00000000000..6c58e6f73a0 --- /dev/null +++ b/sound/core/vmaster.c @@ -0,0 +1,486 @@ +/* + * Virtual master and slave controls + * + * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de> + * + * 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, version 2. + * + */ + +#include <linux/slab.h> +#include <linux/export.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/tlv.h> + +/* + * a subset of information returned via ctl info callback + */ +struct link_ctl_info { + snd_ctl_elem_type_t type; /* value type */ + int count; /* item count */ + int min_val, max_val; /* min, max values */ +}; + +/* + * link master - this contains a list of slave controls that are + * identical types, i.e. info returns the same value type and value + * ranges, but may have different number of counts. + * + * The master control is so far only mono volume/switch for simplicity. + * The same value will be applied to all slaves. + */ +struct link_master { + struct list_head slaves; + struct link_ctl_info info; + int val; /* the master value */ + unsigned int tlv[4]; + void (*hook)(void *private_data, int); + void *hook_private_data; +}; + +/* + * link slave - this contains a slave control element + * + * It fakes the control callbacsk with additional attenuation by the + * master control. A slave may have either one or two channels. + */ + +struct link_slave { + struct list_head list; + struct link_master *master; + struct link_ctl_info info; + int vals[2]; /* current values */ + unsigned int flags; + struct snd_kcontrol *kctl; /* original kcontrol pointer */ + struct snd_kcontrol slave; /* the copy of original control entry */ +}; + +static int slave_update(struct link_slave *slave) +{ + struct snd_ctl_elem_value *uctl; + int err, ch; + + uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; + uctl->id = slave->slave.id; + err = slave->slave.get(&slave->slave, uctl); + for (ch = 0; ch < slave->info.count; ch++) + slave->vals[ch] = uctl->value.integer.value[ch]; + kfree(uctl); + return 0; +} + +/* get the slave ctl info and save the initial values */ +static int slave_init(struct link_slave *slave) +{ + struct snd_ctl_elem_info *uinfo; + int err; + + if (slave->info.count) { + /* already initialized */ + if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE) + return slave_update(slave); + return 0; + } + + uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); + if (!uinfo) + return -ENOMEM; + uinfo->id = slave->slave.id; + err = slave->slave.info(&slave->slave, uinfo); + if (err < 0) { + kfree(uinfo); + return err; + } + slave->info.type = uinfo->type; + slave->info.count = uinfo->count; + if (slave->info.count > 2 || + (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && + slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { + pr_err("ALSA: vmaster: invalid slave element\n"); + kfree(uinfo); + return -EINVAL; + } + slave->info.min_val = uinfo->value.integer.min; + slave->info.max_val = uinfo->value.integer.max; + kfree(uinfo); + + return slave_update(slave); +} + +/* initialize master volume */ +static int master_init(struct link_master *master) +{ + struct link_slave *slave; + + if (master->info.count) + return 0; /* already initialized */ + + list_for_each_entry(slave, &master->slaves, list) { + int err = slave_init(slave); + if (err < 0) + return err; + master->info = slave->info; + master->info.count = 1; /* always mono */ + /* set full volume as default (= no attenuation) */ + master->val = master->info.max_val; + if (master->hook) + master->hook(master->hook_private_data, master->val); + return 1; + } + return -ENOENT; +} + +static int slave_get_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] = slave->vals[ch]; + return 0; +} + +static int slave_put_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch, vol; + + err = master_init(slave->master); + if (err < 0) + return err; + + switch (slave->info.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] &= + !!slave->master->val; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (ch = 0; ch < slave->info.count; ch++) { + /* max master volume is supposed to be 0 dB */ + vol = ucontrol->value.integer.value[ch]; + vol += slave->master->val - slave->master->info.max_val; + if (vol < slave->info.min_val) + vol = slave->info.min_val; + else if (vol > slave->info.max_val) + vol = slave->info.max_val; + ucontrol->value.integer.value[ch] = vol; + } + break; + } + return slave->slave.put(&slave->slave, ucontrol); +} + +/* + * ctl callbacks for slaves + */ +static int slave_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave->slave.info(&slave->slave, uinfo); +} + +static int slave_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave_get_val(slave, ucontrol); +} + +static int slave_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + int err, ch, changed = 0; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) { + if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { + changed = 1; + slave->vals[ch] = ucontrol->value.integer.value[ch]; + } + } + if (!changed) + return 0; + err = slave_put_val(slave, ucontrol); + if (err < 0) + return err; + return 1; +} + +static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, + int op_flag, unsigned int size, + unsigned int __user *tlv) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + /* FIXME: this assumes that the max volume is 0 dB */ + return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); +} + +static void slave_free(struct snd_kcontrol *kcontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + if (slave->slave.private_free) + slave->slave.private_free(&slave->slave); + if (slave->master) + list_del(&slave->list); + kfree(slave); +} + +/* + * Add a slave control to the group with the given master control + * + * All slaves must be the same type (returning the same information + * via info callback). The function doesn't check it, so it's your + * responsibility. + * + * Also, some additional limitations: + * - at most two channels + * - logarithmic volume control (dB level), no linear volume + * - master can only attenuate the volume, no gain + */ +int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave, + unsigned int flags) +{ + struct link_master *master_link = snd_kcontrol_chip(master); + struct link_slave *srec; + + srec = kzalloc(sizeof(*srec) + + slave->count * sizeof(*slave->vd), GFP_KERNEL); + if (!srec) + return -ENOMEM; + srec->kctl = slave; + srec->slave = *slave; + memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); + srec->master = master_link; + srec->flags = flags; + + /* override callbacks */ + slave->info = slave_info; + slave->get = slave_get; + slave->put = slave_put; + if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + slave->tlv.c = slave_tlv_cmd; + slave->private_data = srec; + slave->private_free = slave_free; + + list_add_tail(&srec->list, &master_link->slaves); + return 0; +} +EXPORT_SYMBOL(_snd_ctl_add_slave); + +/* + * ctl callbacks for master controls + */ +static int master_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int ret; + + ret = master_init(master); + if (ret < 0) + return ret; + uinfo->type = master->info.type; + uinfo->count = master->info.count; + uinfo->value.integer.min = master->info.min_val; + uinfo->value.integer.max = master->info.max_val; + return 0; +} + +static int master_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int err = master_init(master); + if (err < 0) + return err; + ucontrol->value.integer.value[0] = master->val; + return 0; +} + +static int sync_slaves(struct link_master *master, int old_val, int new_val) +{ + struct link_slave *slave; + struct snd_ctl_elem_value *uval; + + uval = kmalloc(sizeof(*uval), GFP_KERNEL); + if (!uval) + return -ENOMEM; + list_for_each_entry(slave, &master->slaves, list) { + master->val = old_val; + uval->id = slave->slave.id; + slave_get_val(slave, uval); + master->val = new_val; + slave_put_val(slave, uval); + } + kfree(uval); + return 0; +} + +static int master_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int err, new_val, old_val; + bool first_init; + + err = master_init(master); + if (err < 0) + return err; + first_init = err; + old_val = master->val; + new_val = ucontrol->value.integer.value[0]; + if (new_val == old_val) + return 0; + + err = sync_slaves(master, old_val, new_val); + if (err < 0) + return err; + if (master->hook && !first_init) + master->hook(master->hook_private_data, master->val); + return 1; +} + +static void master_free(struct snd_kcontrol *kcontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave, *n; + + /* free all slave links and retore the original slave kctls */ + list_for_each_entry_safe(slave, n, &master->slaves, list) { + struct snd_kcontrol *sctl = slave->kctl; + struct list_head olist = sctl->list; + memcpy(sctl, &slave->slave, sizeof(*sctl)); + memcpy(sctl->vd, slave->slave.vd, + sctl->count * sizeof(*sctl->vd)); + sctl->list = olist; /* keep the current linked-list */ + kfree(slave); + } + kfree(master); +} + + +/** + * snd_ctl_make_virtual_master - Create a virtual master control + * @name: name string of the control element to create + * @tlv: optional TLV int array for dB information + * + * Creates a virtual master control with the given name string. + * + * After creating a vmaster element, you can add the slave controls + * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached(). + * + * The optional argument @tlv can be used to specify the TLV information + * for dB scale of the master control. It should be a single element + * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or + * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB. + * + * Return: The created control element, or %NULL for errors (ENOMEM). + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv) +{ + struct link_master *master; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new knew; + + memset(&knew, 0, sizeof(knew)); + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.name = name; + knew.info = master_info; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return NULL; + INIT_LIST_HEAD(&master->slaves); + + kctl = snd_ctl_new1(&knew, master); + if (!kctl) { + kfree(master); + return NULL; + } + /* override some callbacks */ + kctl->info = master_info; + kctl->get = master_get; + kctl->put = master_put; + kctl->private_free = master_free; + + /* additional (constant) TLV read */ + if (tlv && + (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE || + tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX || + tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) { + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + memcpy(master->tlv, tlv, sizeof(master->tlv)); + kctl->tlv.p = master->tlv; + } + + return kctl; +} +EXPORT_SYMBOL(snd_ctl_make_virtual_master); + +/** + * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control + * @kcontrol: vmaster kctl element + * @hook: the hook function + * @private_data: the private_data pointer to be saved + * + * Adds the given hook to the vmaster control element so that it's called + * at each time when the value is changed. + * + * Return: Zero. + */ +int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol, + void (*hook)(void *private_data, int), + void *private_data) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + master->hook = hook; + master->hook_private_data = private_data; + return 0; +} +EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook); + +/** + * snd_ctl_sync_vmaster - Sync the vmaster slaves and hook + * @kcontrol: vmaster kctl element + * @hook_only: sync only the hook + * + * Forcibly call the put callback of each slave and call the hook function + * to synchronize with the current value of the given vmaster element. + * NOP when NULL is passed to @kcontrol. + */ +void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only) +{ + struct link_master *master; + bool first_init = false; + + if (!kcontrol) + return; + master = snd_kcontrol_chip(kcontrol); + if (!hook_only) { + int err = master_init(master); + if (err < 0) + return; + first_init = err; + err = sync_slaves(master, master->val, master->val); + if (err < 0) + return; + } + + if (master->hook && !first_init) + master->hook(master->hook_private_data, master->val); +} +EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster); |
