diff options
Diffstat (limited to 'sound/core')
66 files changed, 3878 insertions, 1625 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 45a818002d9..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> @@ -85,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: @@ -92,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;  } @@ -147,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) { @@ -166,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); @@ -186,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) @@ -202,7 +206,7 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,  	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; @@ -220,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) @@ -237,14 +241,14 @@ 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_COMMAND| @@ -279,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;  } @@ -319,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) @@ -334,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, @@ -355,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; @@ -368,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 @@ -375,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)  { @@ -403,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)  { @@ -431,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) @@ -466,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 @@ -474,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) @@ -503,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)  { @@ -530,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) @@ -596,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; @@ -783,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;  		}  	} @@ -877,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, @@ -894,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;  } @@ -908,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;  } @@ -931,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);  } @@ -965,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 : @@ -976,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) @@ -1015,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) @@ -1032,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;  	} @@ -1133,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 { @@ -1219,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;  } @@ -1253,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); @@ -1488,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) @@ -1513,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 426874429a5..b9c0910fb8c 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -83,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; @@ -245,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, @@ -372,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 7730575bfad..886be7da989 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -45,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; @@ -104,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, @@ -125,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 b70564ed8b3..051d55b0552 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -23,12 +23,12 @@  #include <linux/time.h>  #include <linux/mm.h>  #include <linux/slab.h> -#include <linux/smp_lock.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> @@ -89,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; @@ -105,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, ...)  { @@ -153,13 +153,6 @@ 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; @@ -310,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; @@ -353,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;  		} @@ -427,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); @@ -496,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; @@ -532,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; @@ -549,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) @@ -576,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;  } @@ -644,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)) @@ -663,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); @@ -694,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;  } @@ -735,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) @@ -774,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)  { @@ -803,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, @@ -827,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, @@ -858,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;  } @@ -893,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) @@ -949,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)  { @@ -959,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); @@ -986,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 57b792e2439..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,12 +423,12 @@ int snd_card_disconnect(struct snd_card *card)  	/* notify all devices that we are disconnected */  	err = snd_device_disconnect_all(card);  	if (err < 0) -		snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); +		dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number);  	snd_info_card_disconnect(card); -	if (card->card_dev) { -		device_unregister(card->card_dev); -		card->card_dev = NULL; +	if (card->registered) { +		device_del(&card->card_dev); +		card->registered = false;  	}  #ifdef CONFIG_PM  	wake_up(&card->power_sleep); @@ -415,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)  { -	int i, len, idx_flag = 0, loops = SNDRV_CARDS; -	const char *spos, *src; +	const char *spos = name; + +	while (*name) { +		if (isspace(*name) && isalnum(name[1])) +			spos = name + 1; +		name++; +	} +	return spos; +} + +/* return true if the given id string doesn't conflict any other card ids */ +static bool card_id_ok(struct snd_card *card, const char *id) +{ +	int i; +	if (!snd_info_check_reserved_words(id)) +		return false; +	for (i = 0; i < snd_ecards_limit; i++) { +		if (snd_cards[i] && snd_cards[i] != card && +		    !strcmp(snd_cards[i]->id, id)) +			return false; +	} +	return true; +} + +/* copy to card->id only with valid letters from nid */ +static void copy_valid_id_string(struct snd_card *card, const char *src, +				 const char *nid) +{ +	char *id = card->id; + +	while (*nid && !isalnum(*nid)) +		nid++; +	if (isdigit(*nid)) +		*id++ = isalpha(*src) ? *src : 'D'; +	while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { +		if (isalnum(*nid)) +			*id++ = *nid; +		nid++; +	} +	*id = 0; +} + +/* Set card->id from the given string + * If the string conflicts with other ids, add a suffix to make it unique. + */ +static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, +				    const char *nid) +{ +	int len, loops; +	bool is_default = false;  	char *id; -	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));  }  /** @@ -566,7 +605,7 @@ 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); @@ -575,15 +614,15 @@ 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; @@ -598,40 +637,43 @@ 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)) { -			if (card == snd_cards[idx]) -				goto __ok; -			else -				goto __exist; -		} -	}  	strcpy(card->id, buf1);  	snd_info_card_id_change(card); -__ok:  	mutex_unlock(&snd_card_mutex);  	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); +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 @@ -642,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)  { @@ -651,12 +693,11 @@ int snd_card_register(struct snd_card *card)  	if (snd_BUG_ON(!card))  		return -EINVAL; -	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;  	}  	if ((err = snd_device_register_all(card)) < 0) @@ -667,23 +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 -	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; -	} -  	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 4902ae56873..8658578eb58 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -21,23 +21,25 @@  #include <linux/input.h>  #include <linux/slab.h> +#include <linux/module.h>  #include <sound/jack.h>  #include <sound/core.h> -static int jack_switch_types[] = { +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. */ @@ -45,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); @@ -96,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) @@ -108,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); @@ -126,7 +141,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,  	jack->type = type; -	for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) +	for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)  		if (type & (1 << i))  			input_set_capability(jack->input_dev, EV_SW,  					     jack_switch_types[i]); @@ -141,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;  } @@ -152,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.   */ @@ -176,6 +192,9 @@ EXPORT_SYMBOL(snd_jack_set_parent);   * 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 @@ -183,6 +202,8 @@ EXPORT_SYMBOL(snd_jack_set_parent);   * 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) 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 2c41825c836..30e027ecf4d 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -20,6 +20,8 @@   */  #include <linux/init.h> +#include <linux/export.h> +#include <linux/moduleparam.h>  #include <linux/time.h>  #include <linux/slab.h>  #include <linux/ioport.h> @@ -58,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) @@ -85,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); 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 822dd56993c..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> @@ -51,14 +52,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)  					 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; @@ -67,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;  } @@ -190,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; @@ -214,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) @@ -222,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]; @@ -497,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);  } @@ -1043,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); @@ -1058,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)) { @@ -1175,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)); @@ -1189,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); @@ -1200,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; @@ -1331,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;  		} @@ -1353,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 5c8c7dff8ed..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: @@ -654,7 +656,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)  #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; @@ -678,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; @@ -841,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; @@ -851,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;  	} @@ -866,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;  	} @@ -889,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; @@ -910,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, @@ -936,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;  			} @@ -977,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;  	} @@ -1010,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;  	} @@ -1104,7 +1112,8 @@ 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; @@ -1169,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) @@ -1183,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; @@ -1207,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) @@ -1228,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) { @@ -1255,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) @@ -1293,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) @@ -1331,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;  		} @@ -1427,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; @@ -1510,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;  } @@ -1544,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; @@ -1551,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); @@ -1565,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;  		} @@ -1577,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;  		} @@ -1608,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, @@ -1621,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, @@ -1653,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) { @@ -1973,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]; @@ -2193,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; @@ -2205,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;  } @@ -2431,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; @@ -2440,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: @@ -2447,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;  } @@ -2502,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: @@ -2629,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;  } @@ -2656,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 @@ -2676,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; @@ -2755,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))) { @@ -2805,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 */ @@ -2990,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);  	}  } @@ -3076,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 bbe25d8c450..c8171f5783c 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -25,7 +25,7 @@  #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) { @@ -38,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); @@ -51,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 6b4b1287b31..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) @@ -207,13 +213,15 @@ static char *snd_pcm_format_names[] = {  	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)  { -	if (format >= ARRAY_SIZE(snd_pcm_format_names)) +	if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))  		return "Unknown"; -	return snd_pcm_format_names[format]; +	return snd_pcm_format_names[(__force unsigned int)format];  }  EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -269,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) @@ -284,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) @@ -330,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;  	} @@ -390,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);	 @@ -635,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)  { @@ -643,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;  		}  	} @@ -660,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; @@ -673,15 +682,19 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  			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); @@ -695,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; @@ -729,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) { @@ -755,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; @@ -772,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); @@ -940,8 +989,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)  		       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));  	kfree(runtime->hw_constraints.rules);  #ifdef CONFIG_SND_PCM_XRUN_DEBUG -	if (runtime->hwptr_log) -		kfree(runtime->hwptr_log); +	kfree(runtime->hwptr_log);  #endif  	kfree(runtime);  	substream->runtime = NULL; @@ -970,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)  { @@ -993,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: @@ -1021,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);  	} @@ -1045,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);  	} @@ -1064,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 5fb2e28e796..af49721ba0e 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -190,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)); @@ -205,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; @@ -342,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) @@ -364,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 { @@ -426,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 a1707cca9c6..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> @@ -128,7 +130,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram  	}  } -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", @@ -137,6 +140,8 @@ 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 */ @@ -168,8 +173,8 @@ static void xrun(struct snd_pcm_substream *substream)  	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);  	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);  	}  } @@ -179,9 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)  	do {								\  		if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {		\  			xrun_log_show(substream);			\ -			if (printk_ratelimit()) {			\ -				snd_printd("PCM: " fmt, ##args);	\ -			}						\ +			pr_err_ratelimited("ALSA: PCM: " fmt, ##args);	\  			dump_stack_on_xrun(substream);			\  		}							\  	} while (0) @@ -189,6 +192,7 @@ static void xrun(struct snd_pcm_substream *substream)  #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; @@ -204,7 +208,7 @@ struct snd_pcm_hwptr_log {  };  static void xrun_log(struct snd_pcm_substream *substream, -		     snd_pcm_uframes_t pos) +		     snd_pcm_uframes_t pos, int in_interrupt)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	struct snd_pcm_hwptr_log *log = runtime->hwptr_log; @@ -220,10 +224,11 @@ static void xrun_log(struct snd_pcm_substream *substream,  			return;  	}  	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->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; @@ -241,14 +246,16 @@ static void xrun_log_show(struct snd_pcm_substream *substream)  		return;  	if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)  		return; -	pcm_debug_name(substream, name, sizeof(name)); +	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; -		snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " +		pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "  			   "hwptr=%ld/%ld\n", -			   name, entry->jiffies, (unsigned long)entry->pos, +			   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, @@ -262,7 +269,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)  #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */  #define hw_ptr_error(substream, fmt, args...) do { } while (0) -#define xrun_log(substream, pos)	do { } while (0) +#define xrun_log(substream, pos, in_interrupt)	do { } while (0)  #define xrun_log_show(substream)	do { } while (0)  #endif @@ -305,9 +312,29 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;  	snd_pcm_sframes_t hdelta, delta;  	unsigned long jdelta; +	unsigned long curr_jiffies; +	struct timespec curr_tstamp; +	struct timespec audio_tstamp; +	int crossed_boundary = 0;  	old_hw_ptr = runtime->status->hw_ptr; + +	/* +	 * group pointer, time and jiffies reads to allow for more +	 * accurate correlations/corrections. +	 * The values are stored at the end of this routine after +	 * corrections for hw_ptr position +	 */  	pos = substream->ops->pointer(substream); +	curr_jiffies = jiffies; +	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { +		snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + +		if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && +			(substream->ops->wall_clock)) +			substream->ops->wall_clock(substream, &audio_tstamp); +	} +  	if (pos == SNDRV_PCM_POS_XRUN) {  		xrun(substream);  		return -EPIPE; @@ -315,18 +342,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	if (pos >= runtime->buffer_size) {  		if (printk_ratelimit()) {  			char name[16]; -			pcm_debug_name(substream, name, sizeof(name)); +			snd_pcm_debug_name(substream, name, sizeof(name));  			xrun_log_show(substream); -			snd_printd(KERN_ERR  "BUG: %s, pos = %ld, " -				   "buffer size = %ld, period size = %ld\n", -				   name, pos, runtime->buffer_size, -				   runtime->period_size); +			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); +		xrun_log(substream, pos, in_interrupt);  	hw_base = runtime->hw_ptr_base;  	new_hw_ptr = hw_base + pos;  	if (in_interrupt) { @@ -335,11 +362,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  		delta = runtime->hw_ptr_interrupt + runtime->period_size;  		if (delta > new_hw_ptr) {  			/* check for double acknowledged interrupts */ -			hdelta = jiffies - runtime->hw_ptr_jiffies; +			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) +				if (hw_base >= runtime->boundary) {  					hw_base = 0; +					crossed_boundary++; +				}  				new_hw_ptr = hw_base + pos;  				goto __delta;  			} @@ -349,8 +378,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	/* 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) +		if (hw_base >= runtime->boundary) {  			hw_base = 0; +			crossed_boundary++; +		}  		new_hw_ptr = hw_base + pos;  	}        __delta: @@ -360,9 +391,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	if (xrun_debug(substream, in_interrupt ?  			XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {  		char name[16]; -		pcm_debug_name(substream, name, sizeof(name)); -		snd_printd("%s_update: %s: pos=%u/%u/%u, " -			   "hwptr=%ld/%ld/%ld/%ld\n", +		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, @@ -373,6 +404,31 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  			   (unsigned long)new_hw_ptr,  			   (unsigned long)runtime->hw_ptr_base);  	} + +	if (runtime->no_period_wakeup) { +		snd_pcm_sframes_t xrun_threshold; +		/* +		 * Without regular period interrupts, we have to check +		 * the elapsed time to detect xruns. +		 */ +		jdelta = curr_jiffies - runtime->hw_ptr_jiffies; +		if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) +			goto no_delta_check; +		hdelta = jdelta - delta * HZ / runtime->rate; +		xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; +		while (hdelta > xrun_threshold) { +			delta += runtime->buffer_size; +			hw_base += runtime->buffer_size; +			if (hw_base >= runtime->boundary) { +				hw_base = 0; +				crossed_boundary++; +			} +			new_hw_ptr = hw_base + pos; +			hdelta -= runtime->hw_ptr_buffer_jiffies; +		} +		goto no_delta_check; +	} +  	/* something must be really wrong */  	if (delta >= runtime->buffer_size + runtime->period_size) {  		hw_ptr_error(substream, @@ -399,7 +455,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	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) @@ -411,8 +467,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  		/* 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) +			if (new_hw_ptr >= runtime->boundary) {  				new_hw_ptr -= runtime->boundary; +				crossed_boundary--; +			}  			delta--;  		}  		/* align hw_base to buffer_size */ @@ -442,6 +500,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  			     (long)old_hw_ptr);  	} + no_delta_check:  	if (runtime->status->hw_ptr == new_hw_ptr)  		return 0; @@ -460,9 +519,35 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  	}  	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; + +			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);  } @@ -481,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; @@ -579,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)  { @@ -778,7 +865,8 @@ 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, @@ -896,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, @@ -995,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; @@ -1054,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, @@ -1070,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)); @@ -1087,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; @@ -1097,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); @@ -1108,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) @@ -1128,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) @@ -1141,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 @@ -1148,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)  { @@ -1165,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) @@ -1196,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);  } @@ -1230,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, @@ -1263,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, @@ -1294,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, @@ -1322,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, @@ -1352,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, @@ -1364,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)  { @@ -1401,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) @@ -1473,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,  @@ -1519,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, @@ -1547,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) @@ -1584,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;  } @@ -1651,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) @@ -1724,19 +1876,48 @@ static int wait_for_avail(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); +	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; @@ -1755,21 +1936,19 @@ static int wait_for_avail(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->twake) -			break;  	}   _endloop: +	set_current_state(TASK_RUNNING);  	remove_wait_queue(&runtime->tsleep, &wait);  	*availp = avail;  	return err; @@ -1807,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) @@ -1830,13 +2010,12 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,  	}  	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; @@ -1884,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); @@ -2024,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) @@ -2054,13 +2235,12 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,  	}  	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) { @@ -2115,6 +2295,7 @@ 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; @@ -2200,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 917e4055ee3..54debc07f5c 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -25,6 +25,7 @@  #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> @@ -50,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) { @@ -71,6 +64,10 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz  		size >>= 1;  	} while (size >= snd_minimum_buffer);  	dmab->bytes = 0; /* tell error */ +	pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", +		substream->pcm->card->number, substream->pcm->device, +		substream->stream ? 'c' : 'p', substream->number, +		substream->pcm->name, orig_size);  	return 0;  } @@ -81,10 +78,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream  {  	if (substream->dma_buffer.area == NULL)  		return; -	if (substream->dma_buf_id) -		snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id); -	else -		snd_dma_free_pages(&substream->dma_buffer); +	snd_dma_free_pages(&substream->dma_buffer);  	substream->dma_buffer.area = NULL;  } @@ -94,7 +88,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream   *   * Releases the pre-allocated buffer of the given substream.   * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure.   */  int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)  { @@ -114,7 +108,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)   *   * Releases all the pre-allocated buffers on the given pcm.   * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure.   */  int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)  { @@ -253,18 +247,13 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,   * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type   * @substream: the pcm substream instance   * @type: DMA type (SNDRV_DMA_TYPE_*) - * @data: DMA type dependant data + * @data: DMA type dependent data   * @size: the requested pre-allocation size in bytes   * @max: the max. allowed pre-allocation size   *   * Do pre-allocation for the given DMA buffer type.   * - * When substream->dma_buf_id is set, the function tries to look for - * the reserved buffer, and the buffer is not freed but reserved at - * destruction time.  The dma_buf_id must be unique for all systems - * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id(). - * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure.   */  int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,  				  int type, struct device *data, @@ -278,17 +267,17 @@ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,  EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);  /** - * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) + * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)   * @pcm: the pcm instance   * @type: DMA type (SNDRV_DMA_TYPE_*) - * @data: DMA type dependant data + * @data: DMA type dependent data   * @size: the requested pre-allocation size in bytes   * @max: the max. allowed pre-allocation size   *   * Do pre-allocation to all substreams of the given pcm for the   * specified DMA type.   * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure.   */  int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,  					  int type, void *data, @@ -312,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)  { @@ -326,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 */  /** @@ -362,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) @@ -415,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)  { @@ -462,6 +426,8 @@ 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)  { @@ -483,6 +449,8 @@ EXPORT_SYMBOL(snd_pcm_lib_free_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) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 434af3c56d5..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 = {}, @@ -136,6 +140,14 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {  		.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, @@ -209,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;  } @@ -228,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) @@ -247,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)  { @@ -260,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;  } @@ -279,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) @@ -298,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;  } @@ -317,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;  } @@ -337,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) @@ -354,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); @@ -375,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; @@ -445,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)  { @@ -471,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) @@ -484,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 8bc7cb3db33..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> @@ -190,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; @@ -210,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 @@ -255,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 ? ')' : ']');  				} @@ -275,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) { @@ -369,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)  { @@ -391,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)) @@ -423,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; @@ -449,7 +460,7 @@ 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);  	if (pm_qos_request_active(&substream->latency_pm_qos_req))  		pm_qos_remove_request(&substream->latency_pm_qos_req); @@ -458,10 +469,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,  				   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; @@ -509,7 +520,7 @@ 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; +	snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);  	pm_qos_remove_request(&substream->latency_pm_qos_req);  	return result;  } @@ -591,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;  		}  	} @@ -885,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)  { @@ -938,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);  } @@ -952,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)  { @@ -985,7 +1004,7 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)  	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; @@ -1085,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)  { @@ -1107,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)  { @@ -1317,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 = { @@ -1330,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) @@ -1357,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;  } @@ -1376,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;  		} @@ -1479,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;  			} @@ -1516,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 || @@ -1542,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);  }  /* @@ -1573,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 || @@ -1594,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); @@ -1610,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;  } @@ -1959,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))) { @@ -2022,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;  	} @@ -2033,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;  	} @@ -2049,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; @@ -2071,13 +2111,11 @@ 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;  } @@ -2089,7 +2127,10 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)  		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) @@ -2100,13 +2141,15 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)  		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) { @@ -2124,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) { @@ -2138,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; @@ -2382,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; @@ -2414,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; @@ -2561,7 +2610,7 @@ static int snd_pcm_common_ioctl1(struct file *file,  		return res;  	}  	} -	snd_printd("unknown ioctl = 0x%x\n", cmd); +	pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);  	return -ENOTTY;  } @@ -3013,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;  } @@ -3052,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 */ @@ -3153,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) @@ -3174,6 +3227,7 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,  	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 @@ -3182,32 +3236,15 @@ 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;;  	area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -	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);  #endif /* SNDRV_PCM_INFO_MMAP */ -/* mmap callback with pgprot_noncached */ -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, -			       struct vm_area_struct *area) -{ -	area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -	return snd_pcm_default_mmap(substream, area); -} -EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); -  /*   * mmap DMA buffer   */ @@ -3248,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; 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 cbbed0db9e5..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)  { @@ -394,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; @@ -437,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; @@ -455,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: @@ -462,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;  } @@ -530,6 +536,7 @@ 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)  { @@ -606,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) @@ -641,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; @@ -653,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) @@ -668,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) @@ -809,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;  } @@ -870,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) @@ -882,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); @@ -926,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, @@ -975,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) @@ -1006,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) @@ -1029,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)  { @@ -1039,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); @@ -1047,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 @@ -1060,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) @@ -1070,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; @@ -1102,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)  { @@ -1120,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); @@ -1136,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 @@ -1145,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) @@ -1157,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, @@ -1218,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) @@ -1249,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) @@ -1416,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; @@ -1441,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, @@ -1461,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; @@ -1495,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)  { @@ -1560,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; @@ -1577,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 @@ -1589,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++;  		} @@ -1624,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]) { @@ -1641,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;  } @@ -1661,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 @@ -1676,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;  		}  	} @@ -1697,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 a1f1a2f00cc..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 @@ -231,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;  } @@ -255,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 69cd7b3c362..b9184d20c39 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -28,8 +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 @@ -59,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   */ @@ -82,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)); @@ -114,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; @@ -159,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); @@ -179,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; @@ -195,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;  	} @@ -205,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; @@ -236,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) { @@ -246,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) { @@ -256,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; @@ -277,7 +280,6 @@ snd_seq_oss_open(struct file *file, int level)  	client_table[dp->index] = dp;  	num_clients++; -	debug_printk(("open done\n"));  	return 0;   _error: @@ -336,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;  } @@ -352,7 +353,6 @@ delete_port(struct seq_oss_devinfo *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 677dc84590c..3a4569669ef 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -72,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; @@ -153,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; @@ -175,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;  	} @@ -191,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;  	} @@ -406,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; @@ -470,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 73661c4ab82..654d17a5023 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -48,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 ee44ab9593c..701feb71b70 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -24,6 +24,7 @@  #include "seq_oss_midi.h"  #include "../seq_lock.h"  #include <linux/init.h> +#include <linux/module.h>  #include <linux/slab.h>  /* @@ -105,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; @@ -129,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;  		} @@ -137,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 @@ -162,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; @@ -247,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); @@ -255,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++; @@ -325,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 ab59cbfbcaf..4f24ea9fad9 100644 --- a/sound/core/seq/oss/seq_oss_timer.c +++ b/sound/core/seq/oss/seq_oss_timer.c @@ -233,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; @@ -248,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) @@ -276,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/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 99a485f1364..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); @@ -659,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; @@ -684,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; @@ -696,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;  } @@ -708,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; @@ -723,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;  }  /* @@ -740,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; @@ -759,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;  } @@ -772,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 */ @@ -793,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; @@ -1052,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 @@ -2195,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%02x)\n", +	pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",  		   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));  	return -ENOTTY;  } @@ -2407,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;  } @@ -2497,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) 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 c38b90cf3cb..8ce1d0b40dc 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -20,6 +20,7 @@   */  #include <linux/init.h> +#include <linux/export.h>  #include <linux/slab.h>  #include <sound/core.h>  #include "seq_system.h" diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 160b1bd0cd6..e73605393ee 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -57,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); @@ -78,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; @@ -199,7 +199,7 @@ 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;  	} @@ -252,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); @@ -290,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 62a093efb45..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; @@ -146,7 +153,7 @@ 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)) @@ -160,24 +167,14 @@ static int snd_open(struct inode *inode, struct file *file)  			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; -		err = -ENODEV; -	} +	new_fops = fops_get(mptr->f_ops);  	mutex_unlock(&sound_mutex); -	if (err < 0) -		return err; +	if (!new_fops) +		return -ENODEV; +	replace_fops(file, new_fops); -	if (file->f_op->open) { +	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;  } @@ -189,14 +186,22 @@ static const struct file_operations snd_fops =  };  #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; @@ -222,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); @@ -248,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, @@ -268,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]) @@ -325,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)  { @@ -348,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  /* @@ -451,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) { @@ -460,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;  } diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 0c164e5e432..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; @@ -99,8 +105,7 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)  }  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 13afb60999b..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 || @@ -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); @@ -1195,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);  	} @@ -1948,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);  | 
