diff options
Diffstat (limited to 'sound/core')
68 files changed, 4585 insertions, 1961 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 475455c7661..313f22e9d92 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -5,7 +5,9 @@ config SND_TIMER config SND_PCM tristate select SND_TIMER - select GCD + +config SND_DMAENGINE_PCM + tristate config SND_HWDEP tristate @@ -13,6 +15,9 @@ config SND_HWDEP config SND_RAWMIDI tristate +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. @@ -155,6 +160,15 @@ 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" default y @@ -207,6 +221,9 @@ config SND_PCM_XRUN_DEBUG config SND_VMASTER bool +config SND_KCTL_JACK + bool + config SND_DMA_SGBUF def_bool y depends on X86 diff --git a/sound/core/Makefile b/sound/core/Makefile index 350a08d277f..394a38909f6 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -7,13 +7,14 @@ 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_SND_DMA_SGBUF) += sgbuf.o +snd-pcm-dmaengine-objs := pcm_dmaengine.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o @@ -21,13 +22,18 @@ 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 268ab747122..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; @@ -80,6 +86,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) 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; } @@ -142,7 +151,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, 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) { @@ -161,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); @@ -181,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) @@ -191,9 +200,13 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, 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; @@ -211,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) @@ -228,17 +241,18 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, if (ncontrol->name) { strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); if (strcmp(ncontrol->name, kctl.id.name) != 0) - snd_printk(KERN_WARNING - "Control name '%s' truncated to '%s'\n", - ncontrol->name, kctl.id.name); + 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; @@ -269,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; } @@ -309,14 +325,16 @@ 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) @@ -324,10 +342,13 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) 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, @@ -345,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; @@ -358,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 @@ -365,8 +453,8 @@ 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) { @@ -393,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) { @@ -421,8 +509,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id); * * 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_user_ctl(struct snd_ctl_file * file, struct snd_ctl_elem_id *id) @@ -456,6 +544,52 @@ error: } /** + * 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 @@ -464,7 +598,7 @@ error: * 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) @@ -493,10 +627,11 @@ 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) { @@ -520,10 +655,11 @@ 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) @@ -586,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; @@ -773,9 +908,9 @@ static 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; } } @@ -867,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, @@ -884,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; } @@ -898,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; } @@ -921,28 +1082,76 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, 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); } @@ -955,9 +1164,7 @@ 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 < 1) return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : @@ -966,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) @@ -1005,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) @@ -1022,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; } @@ -1099,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); @@ -1123,10 +1342,11 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *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 { @@ -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; } @@ -1243,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); @@ -1387,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, @@ -1477,7 +1700,7 @@ int snd_ctl_create(struct snd_card *card) } /* - * 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) @@ -1502,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 368dc9c4aef..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; 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 a67dfac08c0..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,31 +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; 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 @@ -71,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; if (snd_BUG_ON(!card || !device_data)) - 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 %pF), not found\n", device_data, - __builtin_return_address(0)); - return -ENXIO; + 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; - - if (snd_BUG_ON(!card || !device_data)) - 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 %pF), not found\n", device_data, - __builtin_return_address(0)); - return -ENXIO; + return 0; } /** @@ -150,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; if (snd_BUG_ON(!card || !device_data)) 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; - } + dev = look_for_dev(card, device_data); + if (dev) + return __snd_device_register(dev); snd_BUG(); return -ENXIO; } - EXPORT_SYMBOL(snd_device_register); /* @@ -190,11 +184,9 @@ int snd_device_register_all(struct snd_card *card) 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; } @@ -210,8 +202,8 @@ int snd_device_disconnect_all(struct snd_card *card) if (snd_BUG_ON(!card)) return -ENXIO; - list_for_each_entry(dev, &card->devices, list) { - if (snd_device_disconnect(card, dev->device_data) < 0) + list_for_each_entry_reverse(dev, &card->devices, list) { + if (__snd_device_disconnect(dev) < 0) err = -ENXIO; } return err; @@ -221,23 +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; if (snd_BUG_ON(!card)) - 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; + 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 index 7f4d744ae40..886be7da989 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -19,6 +19,7 @@ */ #include <linux/init.h> +#include <linux/slab.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/hrtimer.h> @@ -44,12 +45,13 @@ 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; - hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); - snd_timer_interrupt(stime->timer, t->sticks); + 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; @@ -103,7 +105,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) } static struct snd_timer_hardware hrtimer_hw = { - .flags = SNDRV_TIMER_HW_AUTO, + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, .open = snd_hrtimer_open, .close = snd_hrtimer_close, .start = snd_hrtimer_start, @@ -124,8 +126,7 @@ static int __init snd_hrtimer_init(void) hrtimer_get_res(CLOCK_MONOTONIC, &tp); if (tp.tv_sec > 0 || !tp.tv_nsec) { - snd_printk(KERN_ERR - "snd-hrtimer: Invalid resolution %u.%09u", + pr_err("snd-hrtimer: Invalid resolution %u.%09u", (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec); return -EINVAL; } diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index a70ee7f1ed9..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,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) if (hw == NULL) return -ENODEV; - 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); @@ -128,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; @@ -147,6 +154,7 @@ 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; } @@ -272,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; @@ -341,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) @@ -360,7 +375,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, *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; @@ -380,6 +395,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, *rhwdep = hwdep; return 0; } +EXPORT_SYMBOL(snd_hwdep_new); static int snd_hwdep_free(struct snd_hwdep *hwdep) { @@ -400,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; } @@ -451,12 +491,15 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) 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; } @@ -525,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 d749a0d394a..051d55b0552 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -22,12 +22,13 @@ #include <linux/init.h> #include <linux/time.h> #include <linux/mm.h> -#include <linux/smp_lock.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> @@ -88,7 +89,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, char *nbuf; nsize = PAGE_ALIGN(nsize); - nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL); + nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO); if (! nbuf) return -ENOMEM; @@ -104,7 +105,7 @@ 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, const char *fmt, ...) { @@ -152,51 +153,48 @@ EXPORT_SYMBOL(snd_seq_root); struct snd_info_entry *snd_oss_root; #endif -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; } @@ -231,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) @@ -281,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) @@ -297,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; @@ -340,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; } @@ -414,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); @@ -483,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,7 +528,7 @@ int __init snd_info_init(void) { struct proc_dir_entry *p; - p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + p = proc_mkdir("asound", NULL); if (p == NULL) return -ENOMEM; snd_proc_root = p; @@ -536,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) @@ -563,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(NULL, snd_proc_root); + proc_remove(snd_proc_root); } return 0; } @@ -631,7 +640,7 @@ void snd_info_card_id_change(struct snd_card *card) { 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)) @@ -650,10 +659,8 @@ void snd_info_card_disconnect(struct snd_card *card) if (!card) return; mutex_lock(&info_mutex); - if (card->proc_root_link) { - snd_remove_proc_entry(snd_proc_root, card->proc_root_link); - card->proc_root_link = NULL; - } + 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); @@ -681,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; } @@ -722,7 +724,7 @@ 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. */ const char *snd_info_get_str(char *dest, const char *src, int len) @@ -761,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) { @@ -790,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, @@ -814,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, @@ -845,7 +847,7 @@ static void snd_info_disconnect(struct snd_info_entry *entry) list_del_init(&entry->list); root = entry->parent == NULL ? snd_proc_root : entry->parent->p; snd_BUG_ON(!root); - snd_remove_proc_entry(root, entry->p); + proc_remove(entry->p); entry->p = NULL; } @@ -880,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) @@ -936,7 +938,7 @@ 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) { @@ -946,15 +948,21 @@ int snd_info_register(struct snd_info_entry * entry) return -ENXIO; root = entry->parent == NULL ? snd_proc_root : entry->parent->p; mutex_lock(&info_mutex); - p = 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); } - 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); @@ -973,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 e4af138d651..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> @@ -93,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 ec4a50ce565..7bdfd19e24a 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -21,11 +21,14 @@ #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> @@ -44,7 +47,8 @@ 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); @@ -63,7 +67,7 @@ static int module_slot_match(struct module *module, int idx) #ifdef MODULE const char *s1, *s2; - if (!module || !module->name || !slots[idx]) + if (!module || !*module->name || !slots[idx]) return 0; s1 = module->name; @@ -91,7 +95,7 @@ static int module_slot_match(struct module *module, int idx) 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 @@ -109,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; @@ -128,8 +132,42 @@ 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_create - create and initialize a soundcard structure + * 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 @@ -142,14 +180,14 @@ static inline int init_info_for_card(struct snd_card *card) * space for the driver to use freely. The allocated struct is stored * in the given card_ret pointer. * - * Returns zero if successful or a negative error code. + * Return: Zero if successful or a negative error code. */ -int snd_card_create(int idx, const char *xid, +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, idx2; + int err; if (snd_BUG_ON(!card_ret)) return -EINVAL; @@ -160,85 +198,81 @@ int snd_card_create(int idx, const char *xid, card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); 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) { - 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_match(module, idx2)) { - idx = idx2; - break; - } - } - } - if (idx < 0) { - for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) - /* idx == -1 == 0xffff means: take any free slot */ - if (~snd_cards_lock & idx & 1<<idx2) { - if (!slots[idx2] || !*slots[idx2]) { - idx = idx2; - break; - } - } - } + 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 (snd_cards_lock & (1 << idx)) + 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_LIST_HEAD(&card->files_list); - init_waitqueue_head(&card->shutdown_sleep); #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 */ err = snd_ctl_create(card); if (err < 0) { - snd_printk(KERN_ERR "unable to register control minors\n"); + dev_err(parent, "unable to register control minors\n"); goto __error; } err = snd_info_card_create(card); if (err < 0) { - snd_printk(KERN_ERR "unable to create card info\n"); + 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); *card_ret = card; return 0; __error_ctl: - snd_device_free_all(card, SNDRV_DEV_CMD_PRE); + snd_device_free_all(card); __error: - kfree(card); + put_device(&card->card_dev); return err; } -EXPORT_SYMBOL(snd_card_create); +EXPORT_SYMBOL(snd_card_new); /* return non-zero if a card is already locked */ int snd_card_locked(int card) @@ -246,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; } @@ -334,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). @@ -342,7 +376,6 @@ 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) @@ -359,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; - snd_cards_lock &= ~(1 << card->number); + 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); list_for_each_entry(mfile, &card->files_list, list) { - file = mfile->file; - /* it's critical part, use endless loop */ /* we have no room to fail */ mfile->disconnected_f_op = mfile->file->f_op; @@ -384,7 +415,7 @@ int snd_card_disconnect(struct snd_card *card) /* 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 @@ -392,15 +423,13 @@ 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); -#ifndef CONFIG_SYSFS_DEPRECATED - if (card->card_dev) { - device_unregister(card->card_dev); - card->card_dev = NULL; + if (card->registered) { + device_del(&card->card_dev); + card->registered = false; } -#endif #ifdef CONFIG_PM wake_up(&card->power_sleep); #endif @@ -417,141 +446,149 @@ 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 */ } + if (card->release_completion) + complete(card->release_completion); kfree(card); return 0; } int snd_card_free_when_closed(struct snd_card *card) { - int free_now = 0; int ret = snd_card_disconnect(card); if (ret) return ret; - - spin_lock(&card->files_lock); - if (list_empty(&card->files_list)) - 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_disconnect(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, list_empty(&card->files_list)); - snd_card_do_free(card); + wait_for_completion(&released); return 0; } - EXPORT_SYMBOL(snd_card_free); -static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) +/* retrieve the last word of shortname or longname */ +static const char *retrieve_id_from_card_name(const char *name) +{ + 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 i, len, idx_flag = 0, loops = SNDRV_CARDS; - const char *spos, *src; + int len, loops; + bool is_default = false; char *id; - if (nid == NULL) { - id = card->shortname; - spos = src = id; - while (*id != '\0') { - if (*id == ' ') - spos = id + 1; - id++; - } - } else { - spos = src = nid; - } + copy_valid_id_string(card, src, nid); id = card->id; - while (*spos != '\0' && !isalnum(*spos)) - spos++; - if (isdigit(*spos)) - *id++ = isalpha(src[0]) ? src[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 set 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++; - *(char *)spos++ = '_'; - *(char *)spos++ = '1'; - *(char *)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)); } /** @@ -568,25 +605,24 @@ void snd_card_set_id(struct snd_card *card, const char *nid) if (card->id[0] != '\0') return; mutex_lock(&snd_card_mutex); - snd_card_set_id_no_lock(card, nid); + snd_card_set_id_no_lock(card, nid, nid); mutex_unlock(&snd_card_mutex); } EXPORT_SYMBOL(snd_card_set_id); -#ifndef CONFIG_SYSFS_DEPRECATED static ssize_t card_id_show_attr(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_card *card = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)"); + 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 = dev_get_drvdata(dev); + 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; @@ -601,15 +637,10 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, memcpy(buf1, buf, copy); buf1[copy] = '\0'; mutex_lock(&snd_card_mutex); - if (!snd_info_check_reserved_words(buf1)) { - __exist: + if (!card_id_ok(NULL, buf1)) { mutex_unlock(&snd_card_mutex); return -EEXIST; } - for (idx = 0; idx < snd_ecards_limit; idx++) { - if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) - goto __exist; - } strcpy(card->id, buf1); snd_info_card_id_change(card); mutex_unlock(&snd_card_mutex); @@ -617,20 +648,32 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, return count; } -static struct device_attribute card_id_attrs = - __ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); +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 = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1); + struct snd_card *card = container_of(dev, struct snd_card, card_dev); + return snprintf(buf, PAGE_SIZE, "%i\n", card->number); } -static struct device_attribute card_number_attrs = - __ATTR(number, S_IRUGO, card_number_show_attr, NULL); -#endif /* CONFIG_SYSFS_DEPRECATED */ +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 @@ -641,7 +684,7 @@ static struct device_attribute card_number_attrs = * 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) { @@ -649,15 +692,14 @@ int snd_card_register(struct snd_card *card) if (snd_BUG_ON(!card)) return -EINVAL; -#ifndef CONFIG_SYSFS_DEPRECATED - if (!card->card_dev) { - card->card_dev = device_create(sound_class, card->dev, - MKDEV(0, 0), card, - "card%i", card->number); - if (IS_ERR(card->card_dev)) - card->card_dev = NULL; + + 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); @@ -666,24 +708,25 @@ int snd_card_register(struct snd_card *card) mutex_unlock(&snd_card_mutex); return 0; } - snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); + 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 -#ifndef CONFIG_SYSFS_DEPRECATED - if (card->card_dev) { - err = device_create_file(card->card_dev, &card_id_attrs); - if (err < 0) - return err; - err = device_create_file(card->card_dev, &card_number_attrs); - if (err < 0) - return err; - } -#endif return 0; } @@ -803,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) @@ -837,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) { @@ -848,6 +891,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENOMEM; mfile->file = file; mfile->disconnected_f_op = NULL; + INIT_LIST_HEAD(&mfile->shutdown_list); spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); @@ -855,6 +899,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENODEV; } list_add(&mfile->list, &card->files_list); + get_device(&card->card_dev); spin_unlock(&card->files_lock); return 0; } @@ -872,36 +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, *found = NULL; - int last_close = 0; spin_lock(&card->files_lock); list_for_each_entry(mfile, &card->files_list, list) { if (mfile->file == file) { 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; } } - if (list_empty(&card->files_list)) - 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 (!found) { - snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); + dev_err(card->dev, "card file remove problem (%p)\n", file); return -ENOENT; } kfree(found); + put_device(&card->card_dev); return 0; } @@ -915,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 950e19ba91f..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,7 +81,7 @@ 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) { @@ -105,7 +106,7 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) 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 index f705eec7372..8658578eb58 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -20,23 +20,26 @@ */ #include <linux/input.h> +#include <linux/slab.h> +#include <linux/module.h> #include <sound/jack.h> #include <sound/core.h> -static int jack_types[] = { +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_free(struct snd_device *device) +static int snd_jack_dev_disconnect(struct snd_device *device) { struct snd_jack *jack = device->device_data; - if (jack->private_free) - jack->private_free(jack); + if (!jack->input_dev) + return 0; /* If the input device is registered with the input subsystem * then we need to use a different deallocator. */ @@ -44,6 +47,18 @@ static int snd_jack_dev_free(struct snd_device *device) 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); @@ -55,7 +70,7 @@ 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; + int err, i; snprintf(jack->name, sizeof(jack->name), "%s %s", card->shortname, jack->id); @@ -65,6 +80,19 @@ static int snd_jack_dev_register(struct snd_device *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; @@ -82,8 +110,8 @@ static int snd_jack_dev_register(struct snd_device *device) * * Creates a new jack object. * - * Returns zero if successful, or a negative error code on failure. - * On success jjack will be initialised. + * 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) @@ -94,6 +122,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, 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); @@ -112,10 +141,10 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, jack->type = type; - for (i = 0; i < ARRAY_SIZE(jack_types); i++) + for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) if (type & (1 << i)) input_set_capability(jack->input_dev, EV_SW, - jack_types[i]); + jack_switch_types[i]); err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); if (err < 0) @@ -127,6 +156,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, fail_input: input_free_device(jack->input_dev); + kfree(jack->id); kfree(jack); return err; } @@ -138,7 +168,7 @@ EXPORT_SYMBOL(snd_jack_new); * @jack: The jack to configure * @parent: The device to set as parent for the jack. * - * Set the parent for the jack input device in the device tree. This + * 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. */ @@ -151,6 +181,48 @@ void snd_jack_set_parent(struct snd_jack *jack, struct device *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 @@ -163,10 +235,19 @@ void snd_jack_report(struct snd_jack *jack, int status) if (!jack) return; - for (i = 0; i < ARRAY_SIZE(jack_types); i++) { + 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_types[i], + input_report_switch(jack->input_dev, + jack_switch_types[i], status & testbit); } diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9e92441f9b7..082509eb805 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -21,59 +21,18 @@ * */ -#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> - -MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Memory allocator for ALSA system."); -MODULE_LICENSE("GPL"); - - -/* - */ - -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 - /* * * Generic memory allocators * */ -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 @@ -81,12 +40,11 @@ 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; if (WARN_ON(!size)) return NULL; @@ -94,9 +52,7 @@ void *snd_malloc_pages(size_t size, gfp_t 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); } /** @@ -113,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); } @@ -128,7 +83,6 @@ 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; if (WARN_ON(!dma)) @@ -138,11 +92,7 @@ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *d | __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 */ @@ -154,9 +104,49 @@ 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); } + +#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 device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; + + dmab->area = NULL; + dmab->addr = 0; + + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + + if (!pool) + return; + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr); +} + +/** + * 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 */ /* @@ -175,9 +165,9 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, * * 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) @@ -192,10 +182,21 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, 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_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; @@ -206,7 +207,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, 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; @@ -228,9 +229,9 @@ 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) @@ -268,6 +269,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #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; @@ -278,260 +284,10 @@ void snd_dma_free_pages(struct snd_dma_buffer *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; - - if (WARN_ON(!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; - - if (WARN_ON(!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" }; - - 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 = proc_create(SND_MEM_PROC_FILE, 0644, NULL, - &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 */ @@ -539,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 23a032c6d48..30e027ecf4d 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -20,7 +20,10 @@ */ #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> @@ -57,26 +60,6 @@ static const char *sanity_file_name(const char *path) else return path; } - -/* print file and line with a certain printk prefix */ -static int print_snd_pfx(unsigned int level, const char *path, int line, - const char *format) -{ - const char *file = sanity_file_name(path); - char tmp[] = "<0>"; - const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT; - int ret = 0; - - if (format[0] == '<' && format[2] == '>') { - tmp[1] = format[1]; - pfx = tmp; - ret = 1; - } - printk("%sALSA %s:%d: ", pfx, file, line); - return ret; -} -#else -#define print_snd_pfx(level, path, line, format) 0 #endif #if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK) @@ -84,15 +67,34 @@ void __snd_printk(unsigned int level, const char *path, int line, const char *format, ...) { va_list args; - -#ifdef CONFIG_SND_DEBUG +#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); - if (print_snd_pfx(level, path, line, format)) - format += 3; /* skip the printk level-prefix */ +#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_GPL(__snd_printk); @@ -101,8 +103,9 @@ EXPORT_SYMBOL_GPL(__snd_printk); #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 @@ -112,18 +115,39 @@ EXPORT_SYMBOL_GPL(__snd_printk); * 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) + if (q->subvendor != vendor) continue; if (!q->subdevice || - (pci->subsystem_device & q->subdevice_mask) == 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/linear.c b/sound/core/oss/linear.c index 4c1d1682719..2045697f449 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -90,11 +90,8 @@ 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; - if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) return -ENXIO; - data = (struct linear_priv *)plugin->extra_data; if (frames == 0) return 0; #ifdef CONFIG_SND_DEBUG @@ -114,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; @@ -140,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); } } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 54e2eb56e4c..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]; @@ -364,7 +378,7 @@ 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) @@ -397,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, }; @@ -492,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); } @@ -577,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) { @@ -613,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) @@ -653,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); @@ -686,7 +703,7 @@ 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); @@ -735,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; @@ -748,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; @@ -761,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; @@ -771,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; @@ -792,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); @@ -808,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)) @@ -821,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; @@ -842,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); @@ -856,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)) @@ -875,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; @@ -920,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); @@ -1034,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); @@ -1049,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)) { @@ -1166,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)); @@ -1180,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); @@ -1191,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; @@ -1322,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; } @@ -1344,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 f7649d4d950..7915564bd39 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -274,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; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d9c96353121..ada69d7a8d7 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -30,7 +30,7 @@ #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> @@ -41,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."); @@ -60,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); @@ -453,8 +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); - if (max < 0) + if (max < 0) { + kfree(save); return max; + } last = 1; } _end: @@ -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; @@ -835,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; @@ -845,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; } @@ -860,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; } @@ -883,19 +892,22 @@ 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); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); if (err < 0) goto failure; @@ -904,9 +916,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) } 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, @@ -930,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; } @@ -971,7 +985,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) 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; } @@ -1004,7 +1018,7 @@ 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; } @@ -1098,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; @@ -1163,12 +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(KERN_DEBUG "pcm_oss: write: " - "recovering from XRUN\n"); - else - printk(KERN_DEBUG "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) @@ -1177,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; @@ -1201,12 +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(KERN_DEBUG "pcm_oss: read: " - "recovering from XRUN\n"); - else - printk(KERN_DEBUG "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) @@ -1222,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) { @@ -1249,12 +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(KERN_DEBUG "pcm_oss: writev: " - "recovering from XRUN\n"); - else - printk(KERN_DEBUG "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) @@ -1287,12 +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(KERN_DEBUG "pcm_oss: readv: " - "recovering from XRUN\n"); - else - printk(KERN_DEBUG "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) @@ -1325,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; } @@ -1421,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; @@ -1504,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; } @@ -1538,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; @@ -1545,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(KERN_DEBUG "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); @@ -1559,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; } @@ -1571,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; } @@ -1602,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(KERN_DEBUG "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, @@ -1615,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(KERN_DEBUG "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, @@ -1647,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) { @@ -1950,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; @@ -1966,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(KERN_DEBUG "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]; @@ -1986,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; @@ -2105,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; @@ -2184,9 +2199,9 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre } #ifdef OSS_DEBUG - printk(KERN_DEBUG "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; @@ -2196,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; } @@ -2370,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) { @@ -2418,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; @@ -2427,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: @@ -2434,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; } @@ -2489,7 +2515,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long if (((cmd >> 8) & 0xff) != 'P') return -EINVAL; #ifdef OSS_DEBUG - printk(KERN_DEBUG "pcm_oss: ioctl = 0x%x\n", cmd); + pr_debug("pcm_oss: ioctl = 0x%x\n", cmd); #endif switch (cmd) { case SNDCTL_DSP_RESET: @@ -2616,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; } @@ -2643,8 +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(KERN_DEBUG "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 @@ -2663,7 +2690,7 @@ 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(KERN_DEBUG "pcm_oss: write %li bytes (wrote %li bytes)\n", + pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result); #endif return result; @@ -2673,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) @@ -2738,7 +2769,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) int err; #ifdef OSS_DEBUG - printk(KERN_DEBUG "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))) { @@ -2788,7 +2819,7 @@ 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(KERN_DEBUG "pcm_oss: mmap ok, bytes = 0x%x\n", + pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes); #endif /* In mmap mode we never stop */ @@ -2964,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, @@ -2972,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); } } @@ -3058,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 6751daa3bb5..727ac44d39f 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -199,12 +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; 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) { @@ -230,13 +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; 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) { @@ -264,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 | @@ -276,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, @@ -306,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) @@ -337,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; } } } @@ -359,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); @@ -641,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; @@ -688,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 b9afab60371..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); diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index 0dcc2870d53..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,7 +51,7 @@ 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; if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) return -ENXIO; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6884ae031f6..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> @@ -47,6 +49,8 @@ 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; } @@ -58,6 +62,8 @@ 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) @@ -203,11 +209,19 @@ 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), }; 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); @@ -263,12 +277,12 @@ static const char *snd_pcm_stream_name(int 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) @@ -278,10 +292,10 @@ static const char *snd_pcm_tstamp_mode_name(int 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) @@ -324,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; } @@ -358,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)); @@ -382,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); @@ -392,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); @@ -415,24 +437,29 @@ 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)); @@ -446,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 @@ -615,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) { @@ -623,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; } } @@ -640,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; @@ -648,23 +677,24 @@ 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); @@ -678,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, const 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; @@ -712,11 +726,12 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device, *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) { @@ -738,12 +753,63 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device, 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; @@ -755,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); @@ -894,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; @@ -921,6 +988,9 @@ 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); @@ -948,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) { @@ -971,7 +1053,7 @@ static int snd_pcm_dev_register(struct snd_device *device) } 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: @@ -999,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); } @@ -1023,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); } @@ -1042,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; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 08bfed594a8..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; @@ -341,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) @@ -363,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 { @@ -425,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 a27545b23ee..9acc77eae48 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -23,8 +23,10 @@ #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> @@ -67,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) % @@ -126,18 +130,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } -#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, 2)) \ - dump_stack(); \ - } while (0) - -static void pcm_debug_name(struct snd_pcm_substream *substream, +#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", @@ -146,6 +140,29 @@ static void pcm_debug_name(struct snd_pcm_substream *substream, 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) { @@ -154,40 +171,111 @@ static void xrun(struct snd_pcm_substream *substream) 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); - if (xrun_debug(substream, 1)) { + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd(KERN_DEBUG "XRUN: %s\n", name); + snd_pcm_debug_name(substream, name, sizeof(name)); + pcm_warn(substream->pcm, "XRUN: %s\n", name); dump_stack_on_xrun(substream); } } -static 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; +}; - pos = substream->ops->pointer(substream); - if (pos == SNDRV_PCM_POS_XRUN) - return pos; /* XRUN */ - if (pos >= runtime->buffer_size) { - if (printk_ratelimit()) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " - "buffer size = 0x%lx, period size = 0x%lx\n", - name, pos, runtime->buffer_size, - runtime->period_size); - } - pos = 0; +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; } - 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 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; @@ -208,89 +296,153 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, 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; } -#define hw_ptr_error(substream, fmt, args...) \ - do { \ - if (xrun_debug(substream, 1)) { \ - if (printk_ratelimit()) { \ - snd_printd("PCM: " fmt, ##args); \ - } \ - dump_stack_on_xrun(substream); \ - } \ - } while (0) - -static 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 old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; + 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; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); + + /* + * 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); + } + if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; } - if (xrun_debug(substream, 8)) { + 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; + } + } + } + /* 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]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, + 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)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); - } - hw_base = runtime->hw_ptr_base; - new_hw_ptr = hw_base + pos; - hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; - delta = new_hw_ptr - hw_ptr_interrupt; - if (hw_ptr_interrupt >= runtime->boundary) { - hw_ptr_interrupt -= runtime->boundary; - if (hw_base < runtime->boundary / 2) - /* hw_base was already lapped; recalc delta */ - delta = new_hw_ptr - hw_ptr_interrupt; - } - if (delta < 0) { - if (runtime->periods == 1 || new_hw_ptr < 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; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value " - "(stream=%i, pos=%ld, intr_ptr=%ld)\n", - substream->stream, (long)pos, - (long)hw_ptr_interrupt); -#if 1 - /* simply skipping the hwptr update seems more - * robust in some cases, e.g. on VMware with - * inaccurate timer source - */ - return 0; /* skip this update */ -#else - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; - delta = 0; -#endif - } else { hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) + 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, 4)) + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check; /* Skip the jiffies check for hardwares with BATCH flag. @@ -299,139 +451,111 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) */ if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) goto no_jiffies_check; - hdelta = new_hw_ptr - old_hw_ptr; + hdelta = delta; if (hdelta < runtime->delay) goto no_jiffies_check; hdelta -= runtime->delay; - jdelta = jiffies - runtime->hw_ptr_jiffies; + 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! [Q] " + "hw_ptr skipping! %s" "(pos=%ld, delta=%ld, period=%ld, " - "jdelta=%lu/%lu/%lu)\n", + "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), delta); - hw_ptr_interrupt = runtime->hw_ptr_interrupt + - runtime->period_size * delta; - if (hw_ptr_interrupt >= runtime->boundary) - hw_ptr_interrupt -= runtime->boundary; - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; + ((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? " - "(stream=%i, delta=%ld, intr_ptr=%ld)\n", + "Lost interrupts? %s" + "(stream=%i, delta=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "", substream->stream, (long)delta, - (long)hw_ptr_interrupt); - /* rebase hw_ptr_interrupt */ - hw_ptr_interrupt = - new_hw_ptr - new_hw_ptr % runtime->period_size; + (long)new_hw_ptr, + (long)old_hw_ptr); } - runtime->hw_ptr_interrupt = hw_ptr_interrupt; + + 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 (runtime->status->hw_ptr == new_hw_ptr) - return 0; - + 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_jiffies = jiffies; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); + 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; - return snd_pcm_update_hw_ptr_post(substream, runtime); + 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_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, hw_base; - snd_pcm_sframes_t delta; - unsigned long jdelta; - - 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; - } - if (xrun_debug(substream, 16)) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)old_hw_ptr, - (unsigned long)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); - } - - hw_base = runtime->hw_ptr_base; - new_hw_ptr = hw_base + pos; - - delta = new_hw_ptr - old_hw_ptr; - jdelta = jiffies - runtime->hw_ptr_jiffies; - if (delta < 0) { - delta += runtime->buffer_size; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value [2] " - "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", - substream->stream, (long)pos, - (long)old_hw_ptr, jdelta); - return 0; - } - hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) - hw_base = 0; - new_hw_ptr = hw_base + pos; - } - /* Do jiffies check only in xrun_debug mode */ - if (!xrun_debug(substream, 4)) - goto no_jiffies_check; - if (delta < runtime->delay) - goto no_jiffies_check; - delta -= runtime->delay; - if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { - hw_ptr_error(substream, - "hw_ptr skipping! " - "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", - (long)pos, (long)delta, - (long)runtime->period_size, jdelta, - ((delta * HZ) / runtime->rate)); - return 0; - } - no_jiffies_check: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, new_hw_ptr); - - if (runtime->status->hw_ptr == new_hw_ptr) - return 0; - - runtime->hw_ptr_base = hw_base; - runtime->status->hw_ptr = new_hw_ptr; - runtime->hw_ptr_jiffies = jiffies; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_hw_ptr0(substream, 0); } /** @@ -442,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; @@ -540,7 +665,8 @@ 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) { @@ -739,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) { @@ -770,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; @@ -784,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; @@ -806,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; @@ -825,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; } @@ -843,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, @@ -942,9 +1084,11 @@ 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; struct snd_interval list_range; @@ -1001,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, @@ -1017,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)); @@ -1034,8 +1180,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, c->private = private; k = 0; while (1) { - if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) + if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { + va_end(args); return -EINVAL; + } c->deps[k++] = dep; if (dep < 0) break; @@ -1044,7 +1192,7 @@ 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); @@ -1055,6 +1203,8 @@ EXPORT_SYMBOL(snd_pcm_hw_rule_add); * @mask: the bitmap mask * * 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) @@ -1075,6 +1225,8 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param * @mask: the 64bit bitmap mask * * 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) @@ -1088,6 +1240,7 @@ 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 - apply an integer constraint to an interval @@ -1095,6 +1248,9 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par * @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) { @@ -1112,6 +1268,9 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); * @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) @@ -1143,14 +1302,16 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, * @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); } @@ -1177,6 +1338,8 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, * @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, @@ -1210,6 +1373,8 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, * @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, @@ -1241,6 +1406,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, * @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, @@ -1269,6 +1436,8 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, * @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, @@ -1299,6 +1468,8 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm * @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, @@ -1311,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) { @@ -1348,8 +1547,8 @@ EXPORT_SYMBOL(_snd_pcm_hw_params_any); * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or %NULL * - * Return the value for field @var 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) @@ -1420,7 +1619,8 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, * * 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, @@ -1466,7 +1666,8 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, * * 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, @@ -1494,6 +1695,8 @@ EXPORT_SYMBOL(snd_pcm_hw_param_last); * 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) @@ -1531,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; } @@ -1598,7 +1803,7 @@ static int snd_pcm_lib_ioctl_fifo_size(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) @@ -1643,7 +1848,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *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) @@ -1663,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; @@ -1671,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; @@ -1702,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; } @@ -1754,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) @@ -1776,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; } @@ -1797,15 +2032,17 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, if (frames > cont) frames = cont; 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; @@ -1826,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); @@ -1834,8 +2072,10 @@ 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; } @@ -1964,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) @@ -1993,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) { @@ -2010,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) @@ -2021,15 +2264,17 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, if (frames > cont) frames = cont; 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; @@ -2050,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; } @@ -2133,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 caa7796bc2f..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,17 +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; - /* 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) { @@ -69,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; } @@ -79,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; } @@ -92,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) { @@ -112,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) { @@ -251,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, @@ -276,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, @@ -310,8 +301,9 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); * @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) { @@ -324,32 +316,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne } EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); - -/* - * compute the max chunk size with continuous pages on sg-buffer - */ -unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, - unsigned int ofs, unsigned int size) -{ - struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream); - unsigned int start, end, pg; - - start = ofs >> PAGE_SHIFT; - end = (ofs + size - 1) >> PAGE_SHIFT; - /* check page continuity */ - pg = sg->table[start].addr >> PAGE_SHIFT; - for (;;) { - start++; - if (start > end) - break; - pg++; - if ((sg->table[start].addr >> PAGE_SHIFT) != pg) - return (start << PAGE_SHIFT) - ofs; - } - /* ok, all on continuous pages */ - return size; -} -EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size); #endif /* CONFIG_SND_DMA_SGBUF */ /** @@ -360,7 +326,7 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size); * 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) @@ -413,7 +379,7 @@ 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) { @@ -434,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 ea2bf82c937..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; } @@ -321,7 +349,7 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width); * @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) @@ -338,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); @@ -359,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; @@ -429,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) { @@ -455,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) @@ -468,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 25b0641e6b8..b653ab001fb 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -20,12 +20,12 @@ */ #include <linux/mm.h> +#include <linux/module.h> #include <linux/file.h> #include <linux/slab.h> -#include <linux/smp_lock.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> @@ -35,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 @@ -139,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), @@ -187,12 +190,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "%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; @@ -207,21 +210,21 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "%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 @@ -252,18 +255,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!doit) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "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 ? ')' : ']'); } @@ -272,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) { @@ -315,10 +318,10 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!params->info) params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; if (!params->fifo_size) { - if (snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) == - snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) && - snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) == - snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) { + 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) @@ -366,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) { @@ -388,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)) @@ -420,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; @@ -446,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; @@ -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,6 +529,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @@ -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; } } @@ -860,6 +877,8 @@ 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,6 +898,8 @@ static struct action_ops snd_pcm_action_start = { /** * snd_pcm_start - start all linked streams * @substream: the PCM substream instance + * + * Return: Zero if successful, or a negative error code. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -917,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 = { @@ -931,8 +953,10 @@ static struct action_ops snd_pcm_action_stop = { * @state: PCM state after stopping the stream * * 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,6 +969,8 @@ EXPORT_SYMBOL(snd_pcm_stop); * * 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) { @@ -973,8 +999,12 @@ 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 betwen the current jiffies, this gives a large enough + * 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; @@ -1002,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) @@ -1059,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 = { @@ -1072,6 +1104,9 @@ static struct action_ops snd_pcm_action_suspend = { * @substream: the PCM substream * * 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) { @@ -1094,6 +1129,8 @@ EXPORT_SYMBOL(snd_pcm_suspend); * @pcm: the PCM instance * * 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) { @@ -1304,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 = { @@ -1317,6 +1354,8 @@ static struct action_ops snd_pcm_action_prepare = { * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags + * + * Return: Zero if successful, or a negative error code. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) @@ -1344,7 +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) { - 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; } @@ -1363,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; } @@ -1466,20 +1515,34 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, break; /* all drained */ init_waitqueue_entry(&wait, current); add_wait_queue(&to_check->sleep, &wait); - set_current_state(TASK_INTERRUPTIBLE); 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; } @@ -1503,13 +1566,11 @@ 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; 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 || @@ -1529,29 +1590,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) } -/* WARNING: Don't forget to fput back the file */ -static struct file *snd_pcm_file_fd(int fd) +static 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); } /* @@ -1560,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 || @@ -1581,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); @@ -1597,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; } @@ -1946,7 +2004,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) if (runtime->dma_bytes) { err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); if (err < 0) - return -EINVAL; + return err; } if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { @@ -1981,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; @@ -2007,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; } @@ -2018,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; } @@ -2034,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; - if (rpcm_file) - *rpcm_file = NULL; - err = snd_pcm_open_substream(pcm, stream, file, &substream); if (err < 0) return err; @@ -2056,38 +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; - if (rpcm_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) { @@ -2105,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) { @@ -2119,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; @@ -2363,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; @@ -2395,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; @@ -2542,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; } @@ -2994,17 +3062,15 @@ static const 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; 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; } @@ -3033,17 +3099,15 @@ static const 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; 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 */ @@ -3134,10 +3198,18 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { /* * mmap the DMA buffer on RAM */ -static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_flags |= VM_RESERVED; +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) @@ -3146,11 +3218,16 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, 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 @@ -3159,20 +3236,10 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, 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_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; - return 0; + return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); @@ -3218,7 +3285,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, if (substream->ops->mmap) err = substream->ops->mmap(substream, area); else - err = snd_pcm_default_mmap(substream, area); + err = snd_pcm_lib_default_mmap(substream, area); if (!err) atomic_inc(&substream->mmap_count); return err; @@ -3258,18 +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 = -ENXIO; - lock_kernel(); pcm_file = file->private_data; substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) - goto out; + return -ENXIO; runtime = substream->runtime; - err = fasync_helper(fd, file, on, &runtime->fasync); -out: - unlock_kernel(); - return err; + return fasync_helper(fd, file, on, &runtime->fasync); } /* @@ -3389,14 +3451,28 @@ out: #endif /* CONFIG_SND_SUPPORT_OLD_API */ #ifndef CONFIG_MMU -unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr, - unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - return 0; +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 dummy_get_unmapped_area NULL +# define snd_pcm_get_unmapped_area NULL #endif /* @@ -3410,12 +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 = dummy_get_unmapped_area, + .get_unmapped_area = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, @@ -3423,11 +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 = dummy_get_unmapped_area, + .get_unmapped_area = snd_pcm_get_unmapped_area, } }; diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index b01d9481d63..20ecd8f1808 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -53,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; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 0f5a194695d..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; @@ -150,12 +147,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs { if (!substream->opened) return; - if (up) { - tasklet_schedule(&substream->runtime->tasklet); - } else { - tasklet_kill(&substream->runtime->tasklet); - substream->ops->trigger(substream, 0); - } + substream->ops->trigger(substream, up); } static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) @@ -163,8 +155,8 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i 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) @@ -180,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) { @@ -195,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; @@ -209,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) { @@ -223,6 +219,7 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) spin_unlock_irqrestore(&runtime->lock, flags); return 0; } +EXPORT_SYMBOL(snd_rawmidi_drain_input); /* look for an available substream for the given stream direction; * if a specific subdevice is given, try to assign it @@ -360,6 +357,7 @@ int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice, module_put(rmidi->card->module); return err; } +EXPORT_SYMBOL(snd_rawmidi_kernel_open); static int snd_rawmidi_open(struct inode *inode, struct file *file) { @@ -376,6 +374,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) 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); @@ -390,8 +392,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) if (rmidi == NULL) return -ENODEV; - if (!try_module_get(rmidi->card->module)) + if (!try_module_get(rmidi->card->module)) { + snd_card_unref(rmidi->card); return -ENXIO; + } mutex_lock(&rmidi->open_mutex); card = rmidi->card; @@ -433,6 +437,10 @@ 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; @@ -451,6 +459,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) #endif file->private_data = rawmidi_file; mutex_unlock(&rmidi->open_mutex); + snd_card_unref(rmidi->card); return 0; __error: @@ -458,6 +467,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) __error_card: mutex_unlock(&rmidi->open_mutex); module_put(rmidi->card->module); + snd_card_unref(rmidi->card); return err; } @@ -526,18 +536,21 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *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; + struct module *module; rfile = file->private_data; rmidi = rfile->rmidi; rawmidi_release_priv(rfile); kfree(rfile); + module = rmidi->card->module; snd_card_file_remove(rmidi->card, file); - module_put(rmidi->card->module); + module_put(module); return 0; } @@ -600,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) @@ -635,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; @@ -647,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) @@ -662,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) @@ -803,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; } @@ -825,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) { @@ -862,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) @@ -874,7 +891,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, 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); @@ -918,13 +936,14 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, } if (result > 0) { if (runtime->event) - tasklet_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, @@ -967,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) @@ -998,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) @@ -1021,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) { @@ -1031,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); @@ -1039,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 @@ -1052,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) @@ -1062,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; @@ -1094,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) { @@ -1112,7 +1138,8 @@ 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); @@ -1128,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 @@ -1137,7 +1165,7 @@ 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) @@ -1149,6 +1177,7 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, 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, @@ -1210,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) @@ -1241,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) @@ -1391,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, @@ -1407,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; @@ -1432,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, @@ -1452,7 +1485,7 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, *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; @@ -1486,6 +1519,7 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, *rrawmidi = rmidi; return 0; } +EXPORT_SYMBOL(snd_rawmidi_new); static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream) { @@ -1551,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; @@ -1568,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 @@ -1580,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++; } @@ -1615,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]) { @@ -1632,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; } @@ -1652,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 @@ -1667,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; } } @@ -1688,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 0851cd13e30..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> @@ -132,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; } @@ -185,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/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index f25e3cc7ddf..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 @@ -220,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 @@ -230,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; } @@ -254,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 c0154a959d5..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_printd 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 9dfb2f77be6..3a4569669ef 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -28,6 +28,7 @@ #include <sound/seq_midi_event.h> #include "../seq_lock.h" #include <linux/init.h> +#include <linux/slab.h> /* @@ -71,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; @@ -152,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; @@ -174,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; } @@ -190,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; } @@ -405,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; @@ -469,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; 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 945a27c34a9..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; @@ -246,7 +247,7 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) if (info->nr_voices > 0) { info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); if (!info->ch) { - snd_printk(KERN_ERR "Cannot malloc\n"); + 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); @@ -254,7 +255,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) } reset_channels(info); } - debug_printk(("synth %d assigned\n", i)); info->opened++; rec->opened++; dp->synth_opened++; @@ -324,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; 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 bf09a5ad186..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) @@ -73,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 8ca2be339f3..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; } @@ -290,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: @@ -300,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); @@ -318,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; @@ -654,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; @@ -679,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; @@ -691,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; } @@ -703,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; @@ -718,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; } /* @@ -735,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; @@ -754,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; } @@ -767,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 */ @@ -788,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; @@ -1047,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 @@ -2190,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; } @@ -2402,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; } @@ -2492,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) @@ -2550,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 c956fe46256..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 */ diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 1f997675c89..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 */ @@ -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) @@ -324,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; @@ -397,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; @@ -412,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); @@ -458,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; } @@ -466,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); } @@ -485,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; } @@ -494,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); } @@ -558,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) @@ -568,5 +569,7 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers); EXPORT_SYMBOL(snd_seq_device_new); EXPORT_SYMBOL(snd_seq_device_register_driver); EXPORT_SYMBOL(snd_seq_device_unregister_driver); +#ifdef CONFIG_MODULES EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); +#endif diff --git a/sound/core/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 0d75afa786b..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; } @@ -124,7 +124,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, 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; 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 7fb55436287..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; } @@ -235,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; } @@ -343,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; } @@ -387,7 +388,7 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) 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; } @@ -430,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); @@ -463,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 ebaf1b541dc..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> @@ -121,7 +121,7 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i runtime = substream->runtime; if ((tmp = runtime->avail) < count) { if (printk_ratelimit()) - snd_printk(KERN_ERR "MIDI output buffer overrun\n"); + pr_err("ALSA: seq_midi: MIDI output buffer overrun\n"); return -ENOMEM; } if (snd_rawmidi_kernel_write(substream, buf, count) < count) @@ -145,7 +145,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, 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); @@ -189,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; @@ -231,7 +231,7 @@ 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)); @@ -362,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 b5d6ea4904c..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> diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 3bf7d73ac52..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" @@ -134,14 +135,14 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *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 */ @@ -412,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 29896ab2340..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; } @@ -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,7 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp) freeprev = cell; } else { #if 0 - printk(KERN_DEBUG "type = %i, source = %i, dest = %i, " + pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, " "client = %i\n", cell->event.type, cell->event.source.client, diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index e7a8e9e4edb..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; } @@ -467,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; 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 f745c317d6a..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; @@ -180,7 +179,7 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) 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; @@ -200,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; } @@ -253,7 +252,7 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, /* 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); @@ -291,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; 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 4e7ec2b4987..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> @@ -101,7 +102,7 @@ void *snd_malloc_sgbuf_pages(struct device *device, if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device, chunk, &tmpb) < 0) { if (!sgbuf->pages) - return NULL; + goto _failed; if (!res_size) goto _failed; size = sgbuf->pages * PAGE_SIZE; @@ -136,3 +137,29 @@ void *snd_malloc_sgbuf_pages(struct device *device, snd_free_sgbuf_pages(dmab); /* free the table */ return NULL; } + +/* + * compute the max chunk size with continuous pages on sg-buffer + */ +unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab, + unsigned int ofs, unsigned int size) +{ + struct snd_sg_buf *sg = dmab->private_data; + unsigned int start, end, pg; + + start = ofs >> PAGE_SHIFT; + end = (ofs + size - 1) >> PAGE_SHIFT; + /* check page continuity */ + pg = sg->table[start].addr >> PAGE_SHIFT; + for (;;) { + start++; + if (start > end) + break; + pg++; + if ((sg->table[start].addr >> PAGE_SHIFT) != pg) + return (start << PAGE_SHIFT) - ofs; + } + /* ok, all on continuous pages */ + return size; +} +EXPORT_SYMBOL(snd_sgbuf_get_chunk_size); diff --git a/sound/core/sound.c b/sound/core/sound.c index 7872a02f6ca..38ad1a0dd3f 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -21,14 +21,12 @@ #include <linux/init.h> #include <linux/slab.h> -#include <linux/smp_lock.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> @@ -100,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) { @@ -110,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; @@ -120,79 +127,81 @@ void *snd_lookup_minor_data(unsigned int minor, int type) EXPORT_SYMBOL(snd_lookup_minor_data); -static int __snd_open(struct inode *inode, struct file *file) +#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_MODULES - 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); - if (file->f_op == NULL) { - file->f_op = old_fops; + 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; } - -/* BKL pushdown: nasty #ifdef avoidance wrapper */ -static int snd_open(struct inode *inode, struct file *file) -{ - int ret; - - lock_kernel(); - ret = __snd_open(inode, file); - unlock_kernel(); - return ret; -} - 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; @@ -218,6 +227,7 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) case SNDRV_DEVICE_TYPE_RAWMIDI: case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: case SNDRV_DEVICE_TYPE_PCM_CAPTURE: + case SNDRV_DEVICE_TYPE_COMPRESS: if (snd_BUG_ON(!card)) return -EINVAL; minor = SNDRV_MINOR(card->number, type + dev); @@ -244,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, @@ -264,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]) @@ -321,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) { @@ -344,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 /* @@ -447,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) { @@ -456,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; } @@ -468,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 7fe12264ff8..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; @@ -93,14 +99,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) default: return -EINVAL; } - if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OSS_MINORS)) + 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; @@ -110,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; @@ -122,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); @@ -169,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 8f8b17ac074..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 */ @@ -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); @@ -326,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) { @@ -351,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); @@ -383,7 +390,7 @@ 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); if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || @@ -393,7 +400,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) 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; @@ -411,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; @@ -530,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; @@ -767,7 +775,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, *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; @@ -806,7 +814,7 @@ static int snd_timer_free(struct snd_timer *timer) 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); @@ -1160,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) @@ -1169,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); } @@ -1194,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); } @@ -1237,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) @@ -1921,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, @@ -1941,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/vmaster.c b/sound/core/vmaster.c index 3b9b550109c..6c58e6f73a0 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -10,6 +10,7 @@ */ #include <linux/slab.h> +#include <linux/export.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> @@ -18,7 +19,7 @@ * a subset of information returned via ctl info callback */ struct link_ctl_info { - int type; /* value type */ + snd_ctl_elem_type_t type; /* value type */ int count; /* item count */ int min_val, max_val; /* min, max values */ }; @@ -36,6 +37,8 @@ struct link_master { struct link_ctl_info info; int val; /* the master value */ unsigned int tlv[4]; + void (*hook)(void *private_data, int); + void *hook_private_data; }; /* @@ -51,6 +54,7 @@ struct link_slave { 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 */ }; @@ -97,7 +101,7 @@ static int slave_init(struct link_slave *slave) if (slave->info.count > 2 || (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { - snd_printk(KERN_ERR "invalid slave element\n"); + pr_err("ALSA: vmaster: invalid slave element\n"); kfree(uinfo); return -EINVAL; } @@ -124,7 +128,9 @@ static int master_init(struct link_master *master) master->info.count = 1; /* always mono */ /* set full volume as default (= no attenuation) */ master->val = master->info.max_val; - return 0; + if (master->hook) + master->hook(master->hook_private_data, master->val); + return 1; } return -ENOENT; } @@ -207,7 +213,10 @@ static int slave_put(struct snd_kcontrol *kcontrol, } if (!changed) return 0; - return slave_put_val(slave, ucontrol); + err = slave_put_val(slave, ucontrol); + if (err < 0) + return err; + return 1; } static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, @@ -233,7 +242,7 @@ static void slave_free(struct snd_kcontrol *kcontrol) * 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 fucntion doesn't check it, so it's your + * via info callback). The function doesn't check it, so it's your * responsibility. * * Also, some additional limitations: @@ -251,6 +260,7 @@ int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave, 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; @@ -300,20 +310,10 @@ static int master_get(struct snd_kcontrol *kcontrol, return 0; } -static int master_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int sync_slaves(struct link_master *master, int old_val, int new_val) { - struct link_master *master = snd_kcontrol_chip(kcontrol); struct link_slave *slave; struct snd_ctl_elem_value *uval; - int err, old_val; - - err = master_init(master); - if (err < 0) - return err; - old_val = master->val; - if (ucontrol->value.integer.value[0] == old_val) - return 0; uval = kmalloc(sizeof(*uval), GFP_KERNEL); if (!uval) @@ -322,20 +322,52 @@ static int master_put(struct snd_kcontrol *kcontrol, master->val = old_val; uval->id = slave->slave.id; slave_get_val(slave, uval); - master->val = ucontrol->value.integer.value[0]; + 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; - - list_for_each_entry(slave, &master->slaves, list) - slave->master = NULL; + 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); } @@ -345,8 +377,7 @@ static void master_free(struct snd_kcontrol *kcontrol) * @name: name string of the control element to create * @tlv: optional TLV int array for dB information * - * Creates a virtual matster control with the given name string. - * Returns the created control element, or NULL for errors (ENOMEM). + * 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(). @@ -355,6 +386,8 @@ static void master_free(struct snd_kcontrol *kcontrol) * 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) @@ -397,3 +430,57 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, 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); |
