aboutsummaryrefslogtreecommitdiff
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/Kconfig88
-rw-r--r--sound/core/Makefile31
-rw-r--r--sound/core/compress_offload.c998
-rw-r--r--sound/core/control.c678
-rw-r--r--sound/core/control_compat.c18
-rw-r--r--sound/core/ctljack.c56
-rw-r--r--sound/core/device.c216
-rw-r--r--sound/core/hrtimer.c166
-rw-r--r--sound/core/hwdep.c129
-rw-r--r--sound/core/info.c266
-rw-r--r--sound/core/info_oss.c15
-rw-r--r--sound/core/init.c620
-rw-r--r--sound/core/isadma.c18
-rw-r--r--sound/core/jack.c260
-rw-r--r--sound/core/memalloc.c527
-rw-r--r--sound/core/memory.c9
-rw-r--r--sound/core/misc.c135
-rw-r--r--sound/core/oss/Makefile7
-rw-r--r--sound/core/oss/copy.c36
-rw-r--r--sound/core/oss/io.c32
-rw-r--r--sound/core/oss/linear.c125
-rw-r--r--sound/core/oss/mixer_oss.c234
-rw-r--r--sound/core/oss/mulaw.c118
-rw-r--r--sound/core/oss/pcm_oss.c388
-rw-r--r--sound/core/oss/pcm_plugin.c140
-rw-r--r--sound/core/oss/pcm_plugin.h17
-rw-r--r--sound/core/oss/plugin_ops.h370
-rw-r--r--sound/core/oss/rate.c52
-rw-r--r--sound/core/oss/route.c25
-rw-r--r--sound/core/pcm.c422
-rw-r--r--sound/core/pcm_compat.c38
-rw-r--r--sound/core/pcm_dmaengine.c369
-rw-r--r--sound/core/pcm_lib.c1435
-rw-r--r--sound/core/pcm_memory.c148
-rw-r--r--sound/core/pcm_misc.c208
-rw-r--r--sound/core/pcm_native.c975
-rw-r--r--sound/core/pcm_timer.c36
-rw-r--r--sound/core/rawmidi.c697
-rw-r--r--sound/core/rtctimer.c21
-rw-r--r--sound/core/seq/Kconfig16
-rw-r--r--sound/core/seq/Makefile31
-rw-r--r--sound/core/seq/instr/Makefile23
-rw-r--r--sound/core/seq/instr/ainstr_fm.c156
-rw-r--r--sound/core/seq/instr/ainstr_gf1.c360
-rw-r--r--sound/core/seq/instr/ainstr_iw.c624
-rw-r--r--sound/core/seq/instr/ainstr_simple.c216
-rw-r--r--sound/core/seq/oss/Makefile2
-rw-r--r--sound/core/seq/oss/seq_oss.c41
-rw-r--r--sound/core/seq/oss/seq_oss_device.h14
-rw-r--r--sound/core/seq/oss/seq_oss_event.c14
-rw-r--r--sound/core/seq/oss/seq_oss_init.c88
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c18
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c24
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c5
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c29
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c8
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c7
-rw-r--r--sound/core/seq/seq.c14
-rw-r--r--sound/core/seq/seq_clientmgr.c127
-rw-r--r--sound/core/seq/seq_clientmgr.h2
-rw-r--r--sound/core/seq/seq_compat.c13
-rw-r--r--sound/core/seq/seq_device.c62
-rw-r--r--sound/core/seq/seq_dummy.c11
-rw-r--r--sound/core/seq/seq_fifo.c20
-rw-r--r--sound/core/seq/seq_info.c2
-rw-r--r--sound/core/seq/seq_instr.c653
-rw-r--r--sound/core/seq/seq_lock.c6
-rw-r--r--sound/core/seq/seq_memory.c32
-rw-r--r--sound/core/seq/seq_memory.h4
-rw-r--r--sound/core/seq/seq_midi.c39
-rw-r--r--sound/core/seq/seq_midi_emul.c23
-rw-r--r--sound/core/seq/seq_midi_event.c111
-rw-r--r--sound/core/seq/seq_ports.c74
-rw-r--r--sound/core/seq/seq_prioq.c20
-rw-r--r--sound/core/seq/seq_queue.c11
-rw-r--r--sound/core/seq/seq_system.c3
-rw-r--r--sound/core/seq/seq_timer.c77
-rw-r--r--sound/core/seq/seq_timer.h7
-rw-r--r--sound/core/seq/seq_virmidi.c12
-rw-r--r--sound/core/sgbuf.c98
-rw-r--r--sound/core/sound.c193
-rw-r--r--sound/core/sound_oss.c42
-rw-r--r--sound/core/timer.c243
-rw-r--r--sound/core/timer_compat.c9
-rw-r--r--sound/core/vmaster.c486
85 files changed, 7773 insertions, 6420 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index b2927523d79..313f22e9d92 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,24 +1,31 @@
# ALSA soundcard-configuration
config SND_TIMER
tristate
- depends on SND
config SND_PCM
tristate
select SND_TIMER
- depends on SND
+
+config SND_DMAENGINE_PCM
+ tristate
config SND_HWDEP
tristate
- depends on SND
config SND_RAWMIDI
tristate
- depends on SND
+
+config SND_COMPRESS_OFFLOAD
+ tristate
+
+# To be effective this also requires INPUT - users should say:
+# select SND_JACK if INPUT=y || INPUT=SND
+# to avoid having to force INPUT on.
+config SND_JACK
+ bool
config SND_SEQUENCER
tristate "Sequencer support"
- depends on SND
select SND_TIMER
help
Say Y or M to enable MIDI sequencer and router support. This
@@ -43,12 +50,11 @@ config SND_SEQ_DUMMY
will be called snd-seq-dummy.
config SND_OSSEMUL
+ select SOUND_OSS_CORE
bool
- depends on SND
config SND_MIXER_OSS
tristate "OSS Mixer API"
- depends on SND
select SND_OSSEMUL
help
To enable OSS mixer API emulation (/dev/mixer*), say Y here
@@ -61,7 +67,6 @@ config SND_MIXER_OSS
config SND_PCM_OSS
tristate "OSS PCM (digital audio) API"
- depends on SND
select SND_OSSEMUL
select SND_PCM
help
@@ -84,7 +89,7 @@ config SND_PCM_OSS_PLUGINS
config SND_SEQUENCER_OSS
bool "OSS Sequencer API"
- depends on SND && SND_SEQUENCER
+ depends on SND_SEQUENCER
select SND_OSSEMUL
help
Say Y here to enable OSS sequencer emulation (both
@@ -96,9 +101,29 @@ config SND_SEQUENCER_OSS
this will be compiled as a module. The module will be called
snd-seq-oss.
+config SND_HRTIMER
+ tristate "HR-timer backend support"
+ depends on HIGH_RES_TIMERS
+ select SND_TIMER
+ help
+ Say Y here to enable HR-timer backend for ALSA timer. ALSA uses
+ the hrtimer as a precise timing source. The ALSA sequencer code
+ also can use this timing source.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hrtimer.
+
+config SND_SEQ_HRTIMER_DEFAULT
+ bool "Use HR-timer as default sequencer timer"
+ depends on SND_HRTIMER && SND_SEQUENCER
+ default y
+ help
+ Say Y here to use the HR-timer backend as the default sequencer
+ timer.
+
config SND_RTCTIMER
tristate "RTC Timer support"
- depends on SND && RTC
+ depends on RTC
select SND_TIMER
help
Say Y here to enable RTC timer support for ALSA. ALSA uses
@@ -109,9 +134,13 @@ config SND_RTCTIMER
To compile this driver as a module, choose M here: the module
will be called snd-rtctimer.
+ Note that this option is exclusive with the new RTC drivers
+ (CONFIG_RTC_CLASS) since this requires the old API.
+
config SND_SEQ_RTCTIMER_DEFAULT
bool "Use RTC as default sequencer timer"
depends on SND_RTCTIMER && SND_SEQUENCER
+ depends on !SND_SEQ_HRTIMER_DEFAULT
default y
help
Say Y here to use the RTC timer as the default sequencer
@@ -123,7 +152,6 @@ config SND_SEQ_RTCTIMER_DEFAULT
config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers"
- depends on SND
help
If you say Y here, the minor numbers of ALSA device files in
/dev/snd/ are allocated dynamically. This allows you to have
@@ -132,9 +160,17 @@ config SND_DYNAMIC_MINORS
If you are unsure about this, say N here.
+config SND_MAX_CARDS
+ int "Max number of sound cards"
+ range 4 256
+ default 32
+ depends on SND_DYNAMIC_MINORS
+ help
+ Specify the max number of sound cards that can be assigned
+ on a single machine.
+
config SND_SUPPORT_OLD_API
bool "Support old ALSA API"
- depends on SND
default y
help
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
@@ -142,16 +178,15 @@ config SND_SUPPORT_OLD_API
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
- depends on SND && PROC_FS
+ depends on PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
- usefull information to developers when a problem occurs). On the
+ useful information to developers when a problem occurs). On the
other side, it makes the ALSA subsystem larger.
config SND_VERBOSE_PRINTK
bool "Verbose printk"
- depends on SND
help
Say Y here to enable verbose log messages. These messages
will help to identify source file and position containing
@@ -161,16 +196,17 @@ config SND_VERBOSE_PRINTK
config SND_DEBUG
bool "Debug"
- depends on SND
help
Say Y here to enable ALSA debug code.
-config SND_DEBUG_DETECT
- bool "Debug detection"
+config SND_DEBUG_VERBOSE
+ bool "More verbose debug"
depends on SND_DEBUG
help
- Say Y here to enable extra-verbose log messages printed when
- detecting devices.
+ Say Y here to enable extra-verbose debugging messages.
+
+ Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
+ So, say Y only if you are ready to be annoyed.
config SND_PCM_XRUN_DEBUG
bool "Enable PCM ring buffer overrun/underrun debugging"
@@ -181,3 +217,15 @@ config SND_PCM_XRUN_DEBUG
It is usually not required, but if you have trouble with
sound clicking when system is loaded, it may help to determine
the process or driver which causes the scheduling gaps.
+
+config SND_VMASTER
+ bool
+
+config SND_KCTL_JACK
+ bool
+
+config SND_DMA_SGBUF
+ def_bool y
+ depends on X86
+
+source "sound/core/seq/Kconfig"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 5a01c76d02e..394a38909f6 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -1,32 +1,39 @@
#
# Makefile for ALSA
-# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-objs := sound.o init.o memory.o info.o control.o misc.o device.o
-ifeq ($(CONFIG_ISA_DMA_API),y)
-snd-objs += isadma.o
-endif
-ifeq ($(CONFIG_SND_OSSEMUL),y)
-snd-objs += sound_oss.o info_oss.o
-endif
+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-objs := memalloc.o sgbuf.o
+snd-pcm-dmaengine-objs := pcm_dmaengine.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
+snd-hrtimer-objs := hrtimer.o
snd-rtctimer-objs := rtctimer.o
snd-hwdep-objs := hwdep.o
+snd-compress-objs := compress_offload.o
+
obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
+obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
-obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_PCM) += snd-pcm.o
+obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
obj-$(CONFIG_SND_OSSEMUL) += oss/
obj-$(CONFIG_SND_SEQUENCER) += seq/
+
+obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
new file mode 100644
index 00000000000..7403f348ed1
--- /dev/null
+++ b/sound/core/compress_offload.c
@@ -0,0 +1,998 @@
+/*
+ * compress_core.c - compress offload core
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+/* TODO:
+ * - add substream support for multiple devices in case of
+ * SND_DYNAMIC_MINORS is not used
+ * - Multiple node representation
+ * driver should be able to register multiple nodes
+ */
+
+static DEFINE_MUTEX(device_mutex);
+
+struct snd_compr_file {
+ unsigned long caps;
+ struct snd_compr_stream stream;
+};
+
+/*
+ * a note on stream states used:
+ * we use follwing states in the compressed core
+ * SNDRV_PCM_STATE_OPEN: When stream has been opened.
+ * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
+ * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
+ * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
+ * decoding/encoding and rendering/capturing data.
+ * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
+ * by calling SNDRV_COMPRESS_DRAIN.
+ * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
+ * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
+ * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
+ */
+static int snd_compr_open(struct inode *inode, struct file *f)
+{
+ struct snd_compr *compr;
+ struct snd_compr_file *data;
+ struct snd_compr_runtime *runtime;
+ enum snd_compr_direction dirn;
+ int maj = imajor(inode);
+ int ret;
+
+ if ((f->f_flags & O_ACCMODE) == O_WRONLY)
+ dirn = SND_COMPRESS_PLAYBACK;
+ else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
+ dirn = SND_COMPRESS_CAPTURE;
+ else
+ return -EINVAL;
+
+ if (maj == snd_major)
+ compr = snd_lookup_minor_data(iminor(inode),
+ SNDRV_DEVICE_TYPE_COMPRESS);
+ else
+ return -EBADFD;
+
+ if (compr == NULL) {
+ pr_err("no device data!!!\n");
+ return -ENODEV;
+ }
+
+ if (dirn != compr->direction) {
+ pr_err("this device doesn't support this direction\n");
+ snd_card_unref(compr->card);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+ data->stream.ops = compr->ops;
+ data->stream.direction = dirn;
+ data->stream.private_data = compr->private_data;
+ data->stream.device = compr;
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime) {
+ kfree(data);
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+ runtime->state = SNDRV_PCM_STATE_OPEN;
+ init_waitqueue_head(&runtime->sleep);
+ data->stream.runtime = runtime;
+ f->private_data = (void *)data;
+ mutex_lock(&compr->lock);
+ ret = compr->ops->open(&data->stream);
+ mutex_unlock(&compr->lock);
+ if (ret) {
+ kfree(runtime);
+ kfree(data);
+ }
+ snd_card_unref(compr->card);
+ return ret;
+}
+
+static int snd_compr_free(struct inode *inode, struct file *f)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_runtime *runtime = data->stream.runtime;
+
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_PAUSED:
+ data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
+ break;
+ default:
+ break;
+ }
+
+ data->stream.ops->free(&data->stream);
+ kfree(data->stream.runtime->buffer);
+ kfree(data->stream.runtime);
+ kfree(data);
+ return 0;
+}
+
+static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp)
+{
+ if (!stream->ops->pointer)
+ return -ENOTSUPP;
+ stream->ops->pointer(stream, tstamp);
+ pr_debug("dsp consumed till %d total %d bytes\n",
+ tstamp->byte_offset, tstamp->copied_total);
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ stream->runtime->total_bytes_transferred = tstamp->copied_total;
+ else
+ stream->runtime->total_bytes_available = tstamp->copied_total;
+ return 0;
+}
+
+static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
+ struct snd_compr_avail *avail)
+{
+ memset(avail, 0, sizeof(*avail));
+ snd_compr_update_tstamp(stream, &avail->tstamp);
+ /* Still need to return avail even if tstamp can't be filled in */
+
+ if (stream->runtime->total_bytes_available == 0 &&
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
+ stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("detected init and someone forgot to do a write\n");
+ return stream->runtime->buffer_size;
+ }
+ pr_debug("app wrote %lld, DSP consumed %lld\n",
+ stream->runtime->total_bytes_available,
+ stream->runtime->total_bytes_transferred);
+ if (stream->runtime->total_bytes_available ==
+ stream->runtime->total_bytes_transferred) {
+ if (stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("both pointers are same, returning full avail\n");
+ return stream->runtime->buffer_size;
+ } else {
+ pr_debug("both pointers are same, returning no avail\n");
+ return 0;
+ }
+ }
+
+ avail->avail = stream->runtime->total_bytes_available -
+ stream->runtime->total_bytes_transferred;
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ avail->avail = stream->runtime->buffer_size - avail->avail;
+
+ pr_debug("ret avail as %lld\n", avail->avail);
+ return avail->avail;
+}
+
+static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
+{
+ struct snd_compr_avail avail;
+
+ return snd_compr_calc_avail(stream, &avail);
+}
+
+static int
+snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_avail ioctl_avail;
+ size_t avail;
+
+ avail = snd_compr_calc_avail(stream, &ioctl_avail);
+ ioctl_avail.avail = avail;
+
+ if (copy_to_user((__u64 __user *)arg,
+ &ioctl_avail, sizeof(ioctl_avail)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_compr_write_data(struct snd_compr_stream *stream,
+ const char __user *buf, size_t count)
+{
+ void *dstn;
+ size_t copy;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ /* 64-bit Modulus */
+ u64 app_pointer = div64_u64(runtime->total_bytes_available,
+ runtime->buffer_size);
+ app_pointer = runtime->total_bytes_available -
+ (app_pointer * runtime->buffer_size);
+
+ dstn = runtime->buffer + app_pointer;
+ pr_debug("copying %ld at %lld\n",
+ (unsigned long)count, app_pointer);
+ if (count < runtime->buffer_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = runtime->buffer_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(runtime->buffer, buf + copy, count - copy))
+ return -EFAULT;
+ }
+ /* if DSP cares, let it know data has been written */
+ if (stream->ops->ack)
+ stream->ops->ack(stream, count);
+ return count;
+}
+
+static ssize_t snd_compr_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ mutex_lock(&stream->device->lock);
+ /* write is allowed when stream is running or has been steup */
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
+ stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
+ mutex_unlock(&stream->device->lock);
+ return -EBADFD;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %ld\n", (unsigned long)avail);
+ /* calculate how much we can write to buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy) {
+ char __user* cbuf = (char __user*)buf;
+ retval = stream->ops->copy(stream, cbuf, avail);
+ } else {
+ retval = snd_compr_write_data(stream, buf, avail);
+ }
+ if (retval > 0)
+ stream->runtime->total_bytes_available += retval;
+
+ /* while initiating the stream, write should be called before START
+ * call, so in setup move state */
+ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
+ stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+ pr_debug("stream prepared, Houston we are good to go\n");
+ }
+
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+
+static ssize_t snd_compr_read(struct file *f, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ mutex_lock(&stream->device->lock);
+
+ /* read is allowed when stream is running, paused, draining and setup
+ * (yes setup is state which we transition to after stop, so if user
+ * wants to read data after stop we allow that)
+ */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_XRUN:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ retval = -EBADFD;
+ goto out;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %ld\n", (unsigned long)avail);
+ /* calculate how much we can read from buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy) {
+ retval = stream->ops->copy(stream, buf, avail);
+ } else {
+ retval = -ENXIO;
+ goto out;
+ }
+ if (retval > 0)
+ stream->runtime->total_bytes_transferred += retval;
+
+out:
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
+{
+ return -ENXIO;
+}
+
+static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
+{
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ return POLLOUT | POLLWRNORM;
+ else
+ return POLLIN | POLLRDNORM;
+}
+
+static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval = 0;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+ stream = &data->stream;
+ if (snd_BUG_ON(!stream))
+ return -EFAULT;
+
+ mutex_lock(&stream->device->lock);
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ retval = -EBADFD;
+ goto out;
+ }
+ poll_wait(f, &stream->runtime->sleep, wait);
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail is %ld\n", (unsigned long)avail);
+ /* check if we have at least one fragment to fill */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ /* stream has been woken up after drain is complete
+ * draining done so set stream state to stopped
+ */
+ retval = snd_compr_get_poll(stream);
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ if (avail >= stream->runtime->fragment_size)
+ retval = snd_compr_get_poll(stream);
+ break;
+ default:
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ retval = POLLOUT | POLLWRNORM | POLLERR;
+ else
+ retval = POLLIN | POLLRDNORM | POLLERR;
+ break;
+ }
+out:
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+static int
+snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_caps caps;
+
+ if (!stream->ops->get_caps)
+ return -ENXIO;
+
+ memset(&caps, 0, sizeof(caps));
+ retval = stream->ops->get_caps(stream, &caps);
+ if (retval)
+ goto out;
+ if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
+ retval = -EFAULT;
+out:
+ return retval;
+}
+
+static int
+snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_codec_caps *caps;
+
+ if (!stream->ops->get_codec_caps)
+ return -ENXIO;
+
+ caps = kzalloc(sizeof(*caps), GFP_KERNEL);
+ if (!caps)
+ return -ENOMEM;
+
+ retval = stream->ops->get_codec_caps(stream, caps);
+ if (retval)
+ goto out;
+ if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
+ retval = -EFAULT;
+
+out:
+ kfree(caps);
+ return retval;
+}
+
+/* revisit this with snd_pcm_preallocate_xxx */
+static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ unsigned int buffer_size;
+ void *buffer;
+
+ buffer_size = params->buffer.fragment_size * params->buffer.fragments;
+ if (stream->ops->copy) {
+ buffer = NULL;
+ /* if copy is defined the driver will be required to copy
+ * the data from core
+ */
+ } else {
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ }
+ stream->runtime->fragment_size = params->buffer.fragment_size;
+ stream->runtime->fragments = params->buffer.fragments;
+ stream->runtime->buffer = buffer;
+ stream->runtime->buffer_size = buffer_size;
+ return 0;
+}
+
+static int snd_compress_check_input(struct snd_compr_params *params)
+{
+ /* first let's check the buffer parameter's */
+ if (params->buffer.fragment_size == 0 ||
+ params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size)
+ return -EINVAL;
+
+ /* now codec parameters */
+ if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
+ return -EINVAL;
+
+ if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_params *params;
+ int retval;
+
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ retval = snd_compress_check_input(params);
+ if (retval)
+ goto out;
+
+ retval = snd_compr_allocate_buffer(stream, params);
+ if (retval) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ retval = stream->ops->set_params(stream, params);
+ if (retval)
+ goto out;
+
+ stream->metadata_set = false;
+ stream->next_track = false;
+
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ else
+ stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+ } else {
+ return -EPERM;
+ }
+out:
+ kfree(params);
+ return retval;
+}
+
+static int
+snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_codec *params;
+ int retval;
+
+ if (!stream->ops->get_params)
+ return -EBADFD;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ retval = stream->ops->get_params(stream, params);
+ if (retval)
+ goto out;
+ if (copy_to_user((char __user *)arg, params, sizeof(*params)))
+ retval = -EFAULT;
+
+out:
+ kfree(params);
+ return retval;
+}
+
+static int
+snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->get_metadata)
+ return -ENXIO;
+
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->get_metadata(stream, &metadata);
+ if (retval != 0)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->set_metadata)
+ return -ENXIO;
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->set_metadata(stream, &metadata);
+ stream->metadata_set = true;
+
+ return retval;
+}
+
+static inline int
+snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_tstamp tstamp = {0};
+ int ret;
+
+ ret = snd_compr_update_tstamp(stream, &tstamp);
+ if (ret == 0)
+ ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
+ &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
+ return ret;
+}
+
+static int snd_compr_pause(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
+ return retval;
+}
+
+static int snd_compr_resume(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ return retval;
+}
+
+static int snd_compr_start(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ return retval;
+}
+
+static int snd_compr_stop(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ if (!retval) {
+ snd_compr_drain_notify(stream);
+ stream->runtime->total_bytes_available = 0;
+ stream->runtime->total_bytes_transferred = 0;
+ }
+ return retval;
+}
+
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+ int ret;
+
+ /*
+ * We are called with lock held. So drop the lock while we wait for
+ * drain complete notfication from the driver
+ *
+ * It is expected that driver will notify the drain completion and then
+ * stream will be moved to SETUP state, even if draining resulted in an
+ * error. We can trigger next track after this.
+ */
+ stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+ mutex_unlock(&stream->device->lock);
+
+ /* we wait for drain to complete here, drain can return when
+ * interruption occurred, wait returned error or success.
+ * For the first two cases we don't do anything different here and
+ * return after waking up
+ */
+
+ ret = wait_event_interruptible(stream->runtime->sleep,
+ (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+ if (ret == -ERESTARTSYS)
+ pr_debug("wait aborted by a signal");
+ else if (ret)
+ pr_debug("wait for drain failed with %d\n", ret);
+
+
+ wake_up(&stream->runtime->sleep);
+ mutex_lock(&stream->device->lock);
+
+ return ret;
+}
+
+static int snd_compr_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
+ if (retval) {
+ pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ return snd_compress_wait_for_drain(stream);
+}
+
+static int snd_compr_next_track(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ /* only a running stream can transition to next track */
+ if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ return -EPERM;
+
+ /* you can signal next track isf this is intended to be a gapless stream
+ * and current track metadata is set
+ */
+ if (stream->metadata_set == false)
+ return -EPERM;
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
+ if (retval != 0)
+ return retval;
+ stream->metadata_set = false;
+ stream->next_track = true;
+ return 0;
+}
+
+static int snd_compr_partial_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+ if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ /* stream can be drained only when next track has been signalled */
+ if (stream->next_track == false)
+ return -EPERM;
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+ if (retval) {
+ pr_debug("Partial drain returned failure\n");
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ stream->next_track = false;
+ return snd_compress_wait_for_drain(stream);
+}
+
+static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ int retval = -ENOTTY;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+ stream = &data->stream;
+ if (snd_BUG_ON(!stream))
+ return -EFAULT;
+ mutex_lock(&stream->device->lock);
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
+ retval = put_user(SNDRV_COMPRESS_VERSION,
+ (int __user *)arg) ? -EFAULT : 0;
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
+ retval = snd_compr_get_caps(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
+ retval = snd_compr_get_codec_caps(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
+ retval = snd_compr_set_params(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
+ retval = snd_compr_get_params(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
+ retval = snd_compr_set_metadata(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
+ retval = snd_compr_get_metadata(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
+ retval = snd_compr_tstamp(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_AVAIL):
+ retval = snd_compr_ioctl_avail(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_PAUSE):
+ retval = snd_compr_pause(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_RESUME):
+ retval = snd_compr_resume(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_START):
+ retval = snd_compr_start(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_STOP):
+ retval = snd_compr_stop(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_DRAIN):
+ retval = snd_compr_drain(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
+ retval = snd_compr_partial_drain(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
+ retval = snd_compr_next_track(stream);
+ break;
+
+ }
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+static const struct file_operations snd_compr_file_ops = {
+ .owner = THIS_MODULE,
+ .open = snd_compr_open,
+ .release = snd_compr_free,
+ .write = snd_compr_write,
+ .read = snd_compr_read,
+ .unlocked_ioctl = snd_compr_ioctl,
+ .mmap = snd_compr_mmap,
+ .poll = snd_compr_poll,
+};
+
+static int snd_compress_dev_register(struct snd_device *device)
+{
+ int ret = -EINVAL;
+ char str[16];
+ struct snd_compr *compr;
+
+ if (snd_BUG_ON(!device || !device->device_data))
+ return -EBADFD;
+ compr = device->device_data;
+
+ sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
+ pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
+ compr->direction);
+ /* register compressed device */
+ ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
+ compr->device, &snd_compr_file_ops, compr, str);
+ if (ret < 0) {
+ pr_err("snd_register_device failed\n %d", ret);
+ return ret;
+ }
+ return ret;
+
+}
+
+static int snd_compress_dev_disconnect(struct snd_device *device)
+{
+ struct snd_compr *compr;
+
+ compr = device->device_data;
+ snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
+ compr->device);
+ return 0;
+}
+
+/*
+ * snd_compress_new: create new compress device
+ * @card: sound card pointer
+ * @device: device number
+ * @dirn: device direction, should be of type enum snd_compr_direction
+ * @compr: compress device pointer
+ */
+int snd_compress_new(struct snd_card *card, int device,
+ int dirn, struct snd_compr *compr)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = NULL,
+ .dev_register = snd_compress_dev_register,
+ .dev_disconnect = snd_compress_dev_disconnect,
+ };
+
+ compr->card = card;
+ compr->device = device;
+ compr->direction = dirn;
+ return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
+}
+EXPORT_SYMBOL_GPL(snd_compress_new);
+
+static int snd_compress_add_device(struct snd_compr *device)
+{
+ int ret;
+
+ if (!device->card)
+ return -EINVAL;
+
+ /* register the card */
+ ret = snd_card_register(device->card);
+ if (ret)
+ goto out;
+ return 0;
+
+out:
+ pr_err("failed with %d\n", ret);
+ return ret;
+
+}
+
+static int snd_compress_remove_device(struct snd_compr *device)
+{
+ return snd_card_free(device->card);
+}
+
+/**
+ * snd_compress_register - register compressed device
+ *
+ * @device: compressed device to register
+ */
+int snd_compress_register(struct snd_compr *device)
+{
+ int retval;
+
+ if (device->name == NULL || device->dev == NULL || device->ops == NULL)
+ return -EINVAL;
+
+ pr_debug("Registering compressed device %s\n", device->name);
+ if (snd_BUG_ON(!device->ops->open))
+ return -EINVAL;
+ if (snd_BUG_ON(!device->ops->free))
+ return -EINVAL;
+ if (snd_BUG_ON(!device->ops->set_params))
+ return -EINVAL;
+ if (snd_BUG_ON(!device->ops->trigger))
+ return -EINVAL;
+
+ mutex_init(&device->lock);
+
+ /* register a compressed card */
+ mutex_lock(&device_mutex);
+ retval = snd_compress_add_device(device);
+ mutex_unlock(&device_mutex);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(snd_compress_register);
+
+int snd_compress_deregister(struct snd_compr *device)
+{
+ pr_debug("Removing compressed device %s\n", device->name);
+ mutex_lock(&device_mutex);
+ snd_compress_remove_device(device);
+ mutex_unlock(&device_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compress_deregister);
+
+static int __init snd_compress_init(void)
+{
+ return 0;
+}
+
+static void __exit snd_compress_exit(void)
+{
+}
+
+module_init(snd_compress_init);
+module_exit(snd_compress_exit);
+
+MODULE_DESCRIPTION("ALSA Compressed offload framework");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/core/control.c b/sound/core/control.c
index 48ef0a09a7a..f0b0e14497a 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1,6 +1,6 @@
/*
* Routines for driver control interface
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,10 +19,9 @@
*
*/
-#include <sound/driver.h>
#include <linux/threads.h>
#include <linux/interrupt.h>
-#include <linux/smp_lock.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
@@ -33,6 +32,7 @@
/* max number of user-defined controls */
#define MAX_USER_CONTROLS 32
+#define MAX_CONTROL_COUNT 1028
struct snd_kctl_ioctl {
struct list_head list; /* list of all ioctls */
@@ -52,6 +52,10 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
struct snd_ctl_file *ctl;
int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
if (!card) {
err = -ENODEV;
@@ -77,11 +81,12 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
- ctl->pid = current->pid;
+ ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
list_add_tail(&ctl->list, &card->ctl_files);
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ snd_card_unref(card);
return 0;
__error:
@@ -89,47 +94,47 @@ 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;
}
static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
+ unsigned long flags;
struct snd_kctl_event *cread;
- spin_lock(&ctl->read_lock);
+ spin_lock_irqsave(&ctl->read_lock, flags);
while (!list_empty(&ctl->events)) {
cread = snd_kctl_event(ctl->events.next);
list_del(&cread->list);
kfree(cread);
}
- spin_unlock(&ctl->read_lock);
+ spin_unlock_irqrestore(&ctl->read_lock, flags);
}
static int snd_ctl_release(struct inode *inode, struct file *file)
{
unsigned long flags;
- struct list_head *list;
struct snd_card *card;
struct snd_ctl_file *ctl;
struct snd_kcontrol *control;
unsigned int idx;
ctl = file->private_data;
- fasync_helper(-1, file, 0, &ctl->fasync);
file->private_data = NULL;
card = ctl->card;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
list_del(&ctl->list);
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
down_write(&card->controls_rwsem);
- list_for_each(list, &card->controls) {
- control = snd_kcontrol(list);
+ list_for_each_entry(control, &card->controls, list)
for (idx = 0; idx < control->count; idx++)
if (control->vd[idx].owner == ctl)
control->vd[idx].owner = NULL;
- }
up_write(&card->controls_rwsem);
snd_ctl_empty_read_queue(ctl);
+ put_pid(ctl->pid);
kfree(ctl);
module_put(card->module);
snd_card_file_remove(card, file);
@@ -140,23 +145,20 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
unsigned long flags;
- struct list_head *flist;
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
- snd_assert(card != NULL && id != NULL, return);
+ if (snd_BUG_ON(!card || !id))
+ return;
read_lock(&card->ctl_files_rwlock);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
- list_for_each(flist, &card->ctl_files) {
- struct list_head *elist;
- ctl = snd_ctl_file(flist);
+ list_for_each_entry(ctl, &card->ctl_files, list) {
if (!ctl->subscribed)
continue;
spin_lock_irqsave(&ctl->read_lock, flags);
- list_for_each(elist, &ctl->events) {
- ev = snd_kctl_event(elist);
+ list_for_each_entry(ev, &ctl->events, list) {
if (ev->id.numid == id->numid) {
ev->mask |= mask;
goto _found;
@@ -168,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);
@@ -188,18 +190,23 @@ 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.
*/
-struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access)
+static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
+ unsigned int access)
{
struct snd_kcontrol *kctl;
unsigned int idx;
- snd_assert(control != NULL, return NULL);
- snd_assert(control->count > 0, return NULL);
+ if (snd_BUG_ON(!control || !control->count))
+ return NULL;
+
+ if (control->count > MAX_CONTROL_COUNT)
+ return NULL;
+
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
if (kctl == NULL) {
- snd_printk(KERN_ERR "Cannot allocate control instance\n");
+ pr_err("ALSA: Cannot allocate control instance\n");
return NULL;
}
*kctl = *control;
@@ -208,8 +215,6 @@ struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int acce
return kctl;
}
-EXPORT_SYMBOL(snd_ctl_new);
-
/**
* snd_ctl_new1 - create a control instance from the template
* @ncontrol: the initialization record
@@ -219,7 +224,7 @@ EXPORT_SYMBOL(snd_ctl_new);
* 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)
@@ -227,23 +232,27 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
struct snd_kcontrol kctl;
unsigned int access;
- snd_assert(ncontrol != NULL, return NULL);
- snd_assert(ncontrol->info != NULL, return NULL);
+ if (snd_BUG_ON(!ncontrol || !ncontrol->info))
+ return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
- if (ncontrol->name)
+ if (ncontrol->name) {
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
+ if (strcmp(ncontrol->name, kctl.id.name) != 0)
+ pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
+ ncontrol->name, kctl.id.name);
+ }
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
- SNDRV_CTL_ELEM_ACCESS_INDIRECT|
- SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
+ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
+ SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
@@ -274,35 +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 list_head *list;
struct snd_kcontrol *kctl;
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(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;
+ /* 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 + 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;
}
@@ -316,25 +325,30 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
* snd_ctl_new1() to the given card. Assigns also an unique
* numid used for fast search.
*
- * Returns zero if successful, or a negative error code on failure.
- *
* It frees automatically the control which cannot be added.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ *
*/
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id;
unsigned int idx;
+ unsigned int count;
int err = -EINVAL;
if (! kcontrol)
return err;
- snd_assert(card != NULL, goto error);
- snd_assert(kcontrol->info != NULL, goto error);
+ if (snd_BUG_ON(!card || !kcontrol->info))
+ goto error;
id = kcontrol->id;
+ if (id.index > UINT_MAX - kcontrol->count)
+ goto error;
+
down_write(&card->controls_rwsem);
if (snd_ctl_find_id(card, &id)) {
up_write(&card->controls_rwsem);
- snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
+ dev_err(card->dev, "control %i:%i:%i:%s:%i is already present\n",
id.iface,
id.device,
id.subdevice,
@@ -352,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;
@@ -365,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
@@ -372,15 +453,16 @@ EXPORT_SYMBOL(snd_ctl_add);
* Removes the control from the card and then releases the instance.
* You don't need to call snd_ctl_free_one(). You must be in
* the write lock - down_write(&card->controls_rwsem).
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id;
unsigned int idx;
- snd_assert(card != NULL && kcontrol != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card || !kcontrol))
+ return -EINVAL;
list_del(&kcontrol->list);
card->controls_count -= kcontrol->count;
id = kcontrol->id;
@@ -399,8 +481,8 @@ EXPORT_SYMBOL(snd_ctl_remove);
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
@@ -421,17 +503,17 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
- * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
+ * snd_ctl_remove_user_ctl - remove and release the unlocked user control
* @file: active control handle
* @id: the control id to remove
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
-static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
- struct snd_ctl_elem_id *id)
+static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
+ struct snd_ctl_elem_id *id)
{
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
@@ -440,18 +522,72 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
if (kctl == NULL) {
- up_write(&card->controls_rwsem);
- return -ENOENT;
+ ret = -ENOENT;
+ goto error;
+ }
+ if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
+ ret = -EINVAL;
+ goto error;
}
for (idx = 0; idx < kctl->count; idx++)
if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
- up_write(&card->controls_rwsem);
- return -EBUSY;
+ ret = -EBUSY;
+ goto error;
}
ret = snd_ctl_remove(card, kctl);
+ if (ret < 0)
+ goto error;
+ card->user_ctl_count--;
+error:
+ up_write(&card->controls_rwsem);
+ return ret;
+}
+
+/**
+ * snd_ctl_activate_id - activate/inactivate the control of the given id
+ * @card: the card instance
+ * @id: the control id to activate/inactivate
+ * @active: non-zero to activate
+ *
+ * Finds the control instance with the given id, and activate or
+ * inactivate the control together with notification, if changed.
+ *
+ * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+ int active)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+ int ret;
+
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+ index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+ vd = &kctl->vd[index_offset];
+ ret = 0;
+ if (active) {
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+ goto unlock;
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
+ goto unlock;
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
+ ret = 1;
+ unlock:
up_write(&card->controls_rwsem);
+ if (ret > 0)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
return ret;
}
+EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
/**
* snd_ctl_rename_id - replace the id of a control on the card
@@ -462,7 +598,7 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
* Finds the control with the old id from the card, and replaces the
* id with the new one.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_ctl_elem_id *dst_id)
@@ -491,19 +627,19 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
*
* Finds the control instance with the given number-id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
* The caller must down card->controls_rwsem before calling this function
* (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
*/
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
- struct list_head *list;
struct snd_kcontrol *kctl;
- snd_assert(card != NULL && numid != 0, return NULL);
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(list);
+ if (snd_BUG_ON(!card || !numid))
+ return NULL;
+ list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
}
@@ -519,22 +655,22 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
*
* Finds the control instance with the given id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
* The caller must down card->controls_rwsem before calling this function
* (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
*/
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
struct snd_ctl_elem_id *id)
{
- struct list_head *list;
struct snd_kcontrol *kctl;
- snd_assert(card != NULL && id != NULL, return NULL);
+ if (snd_BUG_ON(!card || !id))
+ return NULL;
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each(list, &card->controls) {
- kctl = snd_kcontrol(list);
+ list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.iface != id->iface)
continue;
if (kctl->id.device != id->device)
@@ -586,13 +722,12 @@ static int snd_ctl_elem_list(struct snd_card *card,
struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id *dst, *id;
- unsigned int offset, space, first, jidx;
+ unsigned int offset, space, jidx;
if (copy_from_user(&list, _list, sizeof(list)))
return -EFAULT;
offset = list.offset;
space = list.space;
- first = 0;
/* try limit maximum space */
if (space > 16384)
return -ENOMEM;
@@ -664,7 +799,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
#endif
result = kctl->info(kctl, info);
if (result >= 0) {
- snd_assert(info->access == 0, );
+ snd_BUG_ON(info->access);
index_offset = snd_ctl_get_ioff(kctl, &info->id);
vd = &kctl->vd[index_offset];
snd_ctl_build_ioff(&info->id, kctl, index_offset);
@@ -673,7 +808,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
if (vd->owner == ctl)
info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
- info->owner = vd->owner_pid;
+ info->owner = pid_vnr(vd->owner->pid);
} else {
info->owner = -1;
}
@@ -701,12 +836,13 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
return result;
}
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_read(struct snd_card *card,
+ struct snd_ctl_elem_value *control)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result, indirect;
+ int result;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
@@ -715,37 +851,27 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
} else {
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
- indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
- if (control->indirect != indirect) {
- result = -EACCES;
- } else {
- if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->get(kctl, control);
- } else {
- result = -EPERM;
- }
- }
+ if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
+ kctl->get != NULL) {
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ result = kctl->get(kctl, control);
+ } else
+ result = -EPERM;
}
up_read(&card->controls_rwsem);
return result;
}
-EXPORT_SYMBOL(snd_ctl_elem_read);
-
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
struct snd_ctl_elem_value *control;
int result;
-
- control = kmalloc(sizeof(*control), GFP_KERNEL);
- if (control == NULL)
- return -ENOMEM;
- if (copy_from_user(control, _control, sizeof(*control))) {
- kfree(control);
- return -EFAULT;
- }
+
+ control = memdup_user(_control, sizeof(*control));
+ if (IS_ERR(control))
+ return PTR_ERR(control);
+
snd_power_lock(card);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result >= 0)
@@ -758,13 +884,13 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
return result;
}
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
- struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+ struct snd_ctl_elem_value *control)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result, indirect;
+ int result;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
@@ -773,31 +899,25 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
} else {
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
- indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
- if (control->indirect != indirect) {
- result = -EACCES;
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+ kctl->put == NULL ||
+ (file && vd->owner && vd->owner != file)) {
+ result = -EPERM;
} else {
- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
- kctl->put == NULL ||
- (file && vd->owner != NULL && vd->owner != file)) {
- result = -EPERM;
- } else {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->put(kctl, control);
- }
- if (result > 0) {
- up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);
- return 0;
- }
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ 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, &id);
+ return 0;
}
}
up_read(&card->controls_rwsem);
return result;
}
-EXPORT_SYMBOL(snd_ctl_elem_write);
-
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
@@ -805,13 +925,10 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_card *card;
int result;
- control = kmalloc(sizeof(*control), GFP_KERNEL);
- if (control == NULL)
- return -ENOMEM;
- if (copy_from_user(control, _control, sizeof(*control))) {
- kfree(control);
- return -EFAULT;
- }
+ control = memdup_user(_control, sizeof(*control));
+ if (IS_ERR(control))
+ return PTR_ERR(control);
+
card = file->card;
snd_power_lock(card);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
@@ -846,7 +963,6 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
result = -EBUSY;
else {
vd->owner = file;
- vd->owner_pid = current->pid;
result = 0;
}
}
@@ -877,7 +993,6 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
result = -EPERM;
else {
vd->owner = NULL;
- vd->owner_pid = 0;
result = 0;
}
}
@@ -887,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,
@@ -904,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;
}
@@ -918,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;
}
@@ -937,35 +1078,80 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
if (op_flag > 0) {
if (size > 1024 * 128) /* sane value */
return -EINVAL;
- new_data = kmalloc(size, GFP_KERNEL);
- if (new_data == NULL)
- return -ENOMEM;
- if (copy_from_user(new_data, tlv, size)) {
- kfree(new_data);
- return -EFAULT;
- }
+
+ new_data = memdup_user(tlv, size);
+ if (IS_ERR(new_data))
+ return PTR_ERR(new_data);
+ mutex_lock(&ue->card->user_ctl_lock);
change = ue->tlv_data_size != size;
if (!change)
change = memcmp(ue->tlv_data, new_data, size);
kfree(ue->tlv_data);
ue->tlv_data = new_data;
ue->tlv_data_size = size;
+ mutex_unlock(&ue->card->user_ctl_lock);
} else {
- if (! ue->tlv_data_size || ! ue->tlv_data)
- return -ENXIO;
- if (size < ue->tlv_data_size)
- return -ENOSPC;
+ int ret = 0;
+
+ mutex_lock(&ue->card->user_ctl_lock);
+ if (!ue->tlv_data_size || !ue->tlv_data) {
+ ret = -ENXIO;
+ goto err_unlock;
+ }
+ if (size < ue->tlv_data_size) {
+ ret = -ENOSPC;
+ goto err_unlock;
+ }
if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
- return -EFAULT;
+ ret = -EFAULT;
+err_unlock:
+ mutex_unlock(&ue->card->user_ctl_lock);
+ if (ret)
+ return ret;
}
return change;
}
+static int snd_ctl_elem_init_enum_names(struct user_element *ue)
+{
+ char *names, *p;
+ size_t buf_len, name_len;
+ unsigned int i;
+ const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
+
+ if (ue->info.value.enumerated.names_length > 64 * 1024)
+ return -EINVAL;
+
+ names = memdup_user((const void __user *)user_ptrval,
+ ue->info.value.enumerated.names_length);
+ if (IS_ERR(names))
+ return PTR_ERR(names);
+
+ /* check that there are enough valid names */
+ buf_len = ue->info.value.enumerated.names_length;
+ p = names;
+ for (i = 0; i < ue->info.value.enumerated.items; ++i) {
+ name_len = strnlen(p, buf_len);
+ if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
+ kfree(names);
+ return -EINVAL;
+ }
+ p += name_len + 1;
+ buf_len -= name_len + 1;
+ }
+
+ ue->priv_data = names;
+ ue->info.value.enumerated.names_ptr = 0;
+
+ return 0;
+}
+
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
struct user_element *ue = kcontrol->private_data;
- if (ue->tlv_data)
- kfree(ue->tlv_data);
+
+ kfree(ue->tlv_data);
+ kfree(ue->priv_data);
kfree(ue);
}
@@ -978,10 +1164,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
long private_size;
struct user_element *ue;
int idx, err;
-
- if (card->user_ctl_count >= MAX_USER_CONTROLS)
- return -ENOMEM;
- if (info->count > 1024)
+
+ if (info->count < 1)
return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
@@ -989,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)
@@ -1028,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)
@@ -1045,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;
}
@@ -1082,18 +1278,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file,
struct snd_ctl_elem_id __user *_id)
{
struct snd_ctl_elem_id id;
- int err;
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- err = snd_ctl_remove_unlocked_id(file, &id);
- if (! err) {
- struct snd_card *card = file->card;
- down_write(&card->controls_rwsem);
- card->user_ctl_count--;
- up_write(&card->controls_rwsem);
- }
- return err;
+ return snd_ctl_remove_user_ctl(file, &id);
}
static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
@@ -1130,7 +1318,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
return -EFAULT;
- if (tlv.length < sizeof(unsigned int) * 3)
+ if (tlv.length < sizeof(unsigned int) * 2)
return -EINVAL;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_numid(card, tlv.numid);
@@ -1150,14 +1338,15 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
goto __kctl_end;
}
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- if (file && vd->owner != NULL && vd->owner != file) {
+ if (vd->owner != NULL && vd->owner != file) {
err = -EPERM;
goto __kctl_end;
}
- err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
+ err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
if (err > 0) {
+ struct snd_ctl_elem_id id = kctl->id;
up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id);
return 0;
}
} else {
@@ -1182,7 +1371,6 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
{
struct snd_ctl_file *ctl;
struct snd_card *card;
- struct list_head *list;
struct snd_kctl_ioctl *p;
void __user *argp = (void __user *)arg;
int __user *ip = argp;
@@ -1190,7 +1378,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
ctl = file->private_data;
card = ctl->card;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
@@ -1232,8 +1421,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
#endif
}
down_read(&snd_ioctl_rwsem);
- list_for_each(list, &snd_control_ioctls) {
- p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
@@ -1241,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,7 +1441,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
ssize_t result = 0;
ctl = file->private_data;
- snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!ctl || !ctl->card))
+ return -ENXIO;
if (!ctl->subscribed)
return -EBADFD;
if (count < sizeof(struct snd_ctl_event))
@@ -1274,8 +1463,10 @@ 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 result > 0 ? result : -ERESTARTSYS;
+ return -ERESTARTSYS;
spin_lock_irq(&ctl->read_lock);
}
kev = snd_kctl_event(ctl->events.next);
@@ -1357,13 +1548,12 @@ EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
struct list_head *lists)
{
- struct list_head *list;
struct snd_kctl_ioctl *p;
- snd_assert(fcn != NULL, return -EINVAL);
+ if (snd_BUG_ON(!fcn))
+ return -EINVAL;
down_write(&snd_ioctl_rwsem);
- list_for_each(list, lists) {
- p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
list_del(&p->list);
up_write(&snd_ioctl_rwsem);
@@ -1395,12 +1585,9 @@ EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
static int snd_ctl_fasync(int fd, struct file * file, int on)
{
struct snd_ctl_file *ctl;
- int err;
+
ctl = file->private_data;
- err = fasync_helper(fd, file, on, &ctl->fasync);
- if (err < 0)
- return err;
- return 0;
+ return fasync_helper(fd, file, on, &ctl->fasync);
}
/*
@@ -1416,12 +1603,13 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
* INIT PART
*/
-static struct file_operations snd_ctl_f_ops =
+static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
+ .llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
@@ -1437,9 +1625,11 @@ static int snd_ctl_dev_register(struct snd_device *device)
int err, cardnum;
char name[16];
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
cardnum = card->number;
- snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+ return -ENXIO;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
@@ -1453,21 +1643,21 @@ static int snd_ctl_dev_register(struct snd_device *device)
static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
- struct list_head *flist;
struct snd_ctl_file *ctl;
int err, cardnum;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
cardnum = card->number;
- snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+ return -ENXIO;
- down_read(&card->controls_rwsem);
- list_for_each(flist, &card->ctl_files) {
- ctl = snd_ctl_file(flist);
+ read_lock(&card->ctl_files_rwlock);
+ list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
card, -1)) < 0)
@@ -1504,6 +1694,62 @@ int snd_ctl_create(struct snd_card *card)
.dev_disconnect = snd_ctl_dev_disconnect,
};
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
+
+/*
+ * Frequently used control callbacks/helpers
+ */
+int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+
+int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+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 ab48962c48c..b9c0910fb8c 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -21,6 +21,7 @@
/* this file included from control.c */
#include <linux/compat.h>
+#include <linux/slab.h>
struct snd_ctl_elem_list32 {
u32 offset;
@@ -82,6 +83,8 @@ struct snd_ctl_elem_info32 {
u32 items;
u32 item;
char name[64];
+ u64 names_ptr;
+ u32 names_length;
} enumerated;
unsigned char reserved[128];
} value;
@@ -219,7 +222,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value32 __user *data32,
int *typep, int *countp)
{
- int i, type, count, size;
+ int i, type, size;
+ int uninitialized_var(count);
unsigned int indirect;
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
@@ -243,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,
@@ -370,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;
@@ -392,12 +398,13 @@ enum {
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
- struct list_head *list;
+ struct snd_kctl_ioctl *p;
void __user *argp = compat_ptr(arg);
int err;
ctl = file->private_data;
- snd_assert(ctl && ctl->card, return -ENXIO);
+ if (snd_BUG_ON(!ctl || !ctl->card))
+ return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
@@ -427,8 +434,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
}
down_read(&snd_ioctl_rwsem);
- list_for_each(list, &snd_control_compat_ioctls) {
- struct snd_kctl_ioctl *p = list_entry(list, struct snd_kctl_ioctl, list);
+ list_for_each_entry(p, &snd_control_compat_ioctls, list) {
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
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 ccb25816ac9..41bec3075ae 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -1,6 +1,6 @@
/*
* Device management routines
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,9 +19,9 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/export.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -39,32 +39,75 @@
* The data pointer plays a role as the identifier, too, so the
* pointer address must be unique and unchanged.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_device_new(struct snd_card *card, snd_device_type_t type,
+int snd_device_new(struct snd_card *card, enum snd_device_type type,
void *device_data, struct snd_device_ops *ops)
{
struct snd_device *dev;
+ struct list_head *p;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
- snd_assert(ops != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card || !device_data || !ops))
+ return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
- snd_printk(KERN_ERR "Cannot allocate device\n");
+ dev_err(card->dev, "Cannot allocate device, type=%d\n", type);
return -ENOMEM;
}
+ INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
- list_add(&dev->list, &card->devices); /* add to the head of list */
+
+ /* insert the entry in an incrementally sorted list */
+ list_for_each_prev(p, &card->devices) {
+ struct snd_device *pdev = list_entry(p, struct snd_device, list);
+ if ((unsigned int)pdev->type <= (unsigned int)type)
+ break;
+ }
+
+ list_add(&dev->list, p);
return 0;
}
-
EXPORT_SYMBOL(snd_device_new);
+static int __snd_device_disconnect(struct snd_device *dev)
+{
+ if (dev->state == SNDRV_DEV_REGISTERED) {
+ if (dev->ops->dev_disconnect &&
+ dev->ops->dev_disconnect(dev))
+ dev_err(dev->card->dev, "device disconnect failure\n");
+ dev->state = SNDRV_DEV_DISCONNECTED;
+ }
+ return 0;
+}
+
+static void __snd_device_free(struct snd_device *dev)
+{
+ /* unlink */
+ list_del(&dev->list);
+
+ __snd_device_disconnect(dev);
+ if (dev->ops->dev_free) {
+ if (dev->ops->dev_free(dev))
+ dev_err(dev->card->dev, "device free failure\n");
+ }
+ kfree(dev);
+}
+
+static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
+{
+ struct snd_device *dev;
+
+ list_for_each_entry(dev, &card->devices, list)
+ if (dev->device_data == device_data)
+ return dev;
+
+ return NULL;
+}
+
/**
* snd_device_free - release the device from the card
* @card: the card instance
@@ -73,77 +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 list_head *list;
struct snd_device *dev;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (dev->device_data != device_data)
- continue;
- /* unlink */
- list_del(&dev->list);
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect)
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR
- "device disconnect failure\n");
- if (dev->ops->dev_free) {
- if (dev->ops->dev_free(dev))
- snd_printk(KERN_ERR "device free failure\n");
- }
- kfree(dev);
- return 0;
- }
- snd_printd("device free %p (from %p), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
+ if (snd_BUG_ON(!card || !device_data))
+ return;
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ __snd_device_free(dev);
+ else
+ dev_dbg(card->dev, "device free %p (from %pF), not found\n",
+ device_data, __builtin_return_address(0));
}
-
EXPORT_SYMBOL(snd_device_free);
-/**
- * snd_device_disconnect - disconnect the device
- * @card: the card instance
- * @device_data: the data pointer to disconnect
- *
- * Turns the device into the disconnection state, invoking
- * dev_disconnect callback, if the device was already registered.
- *
- * Usually called from snd_card_disconnect().
- *
- * Returns zero if successful, or a negative error code on failure or if the
- * device not found.
- */
-int snd_device_disconnect(struct snd_card *card, void *device_data)
+static int __snd_device_register(struct snd_device *dev)
{
- struct list_head *list;
- struct snd_device *dev;
-
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect) {
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR "device disconnect failure\n");
- dev->state = SNDRV_DEV_DISCONNECTED;
+ if (dev->state == SNDRV_DEV_BUILD) {
+ if (dev->ops->dev_register) {
+ int err = dev->ops->dev_register(dev);
+ if (err < 0)
+ return err;
}
- return 0;
+ dev->state = SNDRV_DEV_REGISTERED;
}
- snd_printd("device disconnect %p (from %p), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
+ return 0;
}
/**
@@ -156,34 +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 list_head *list;
struct snd_device *dev;
- int err;
- snd_assert(card != NULL, return -ENXIO);
- snd_assert(device_data != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
- if ((err = dev->ops->dev_register(dev)) < 0)
- return err;
- dev->state = SNDRV_DEV_REGISTERED;
- return 0;
- }
- snd_printd("snd_device_register busy\n");
- return -EBUSY;
- }
+ if (snd_BUG_ON(!card || !device_data))
+ return -ENXIO;
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ return __snd_device_register(dev);
snd_BUG();
return -ENXIO;
}
-
EXPORT_SYMBOL(snd_device_register);
/*
@@ -192,18 +178,15 @@ EXPORT_SYMBOL(snd_device_register);
*/
int snd_device_register_all(struct snd_card *card)
{
- struct list_head *list;
struct snd_device *dev;
int err;
- snd_assert(card != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(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;
- }
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ list_for_each_entry(dev, &card->devices, list) {
+ err = __snd_device_register(dev);
+ if (err < 0)
+ return err;
}
return 0;
}
@@ -215,13 +198,12 @@ int snd_device_register_all(struct snd_card *card)
int snd_device_disconnect_all(struct snd_card *card)
{
struct snd_device *dev;
- struct list_head *list;
int err = 0;
- snd_assert(card != NULL, return -ENXIO);
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (snd_device_disconnect(card, dev->device_data) < 0)
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ list_for_each_entry_reverse(dev, &card->devices, list) {
+ if (__snd_device_disconnect(dev) < 0)
err = -ENXIO;
}
return err;
@@ -231,24 +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;
- struct list_head *list;
- int err;
- unsigned int range_low, range_high;
-
- snd_assert(card != NULL, return -ENXIO);
- range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
- range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
- __again:
- list_for_each(list, &card->devices) {
- dev = snd_device(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;
+ struct snd_device *dev, *next;
+
+ if (snd_BUG_ON(!card))
+ return;
+ list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
+ __snd_device_free(dev);
}
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
new file mode 100644
index 00000000000..886be7da989
--- /dev/null
+++ b/sound/core/hrtimer.c
@@ -0,0 +1,166 @@
+/*
+ * ALSA timer back-end using hrtimer
+ * Copyright (C) 2008 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hrtimer.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA hrtimer backend");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
+
+#define NANO_SEC 1000000000UL /* 10^9 in sec */
+static unsigned int resolution;
+
+struct snd_hrtimer {
+ struct snd_timer *timer;
+ struct hrtimer hrt;
+ atomic_t running;
+};
+
+static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
+ struct snd_timer *t = stime->timer;
+ unsigned long oruns;
+
+ if (!atomic_read(&stime->running))
+ return HRTIMER_NORESTART;
+
+ oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
+ snd_timer_interrupt(stime->timer, t->sticks * oruns);
+
+ if (!atomic_read(&stime->running))
+ return HRTIMER_NORESTART;
+ return HRTIMER_RESTART;
+}
+
+static int snd_hrtimer_open(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime;
+
+ stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ if (!stime)
+ return -ENOMEM;
+ hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ stime->timer = t;
+ stime->hrt.function = snd_hrtimer_callback;
+ atomic_set(&stime->running, 0);
+ t->private_data = stime;
+ return 0;
+}
+
+static int snd_hrtimer_close(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ if (stime) {
+ hrtimer_cancel(&stime->hrt);
+ kfree(stime);
+ t->private_data = NULL;
+ }
+ return 0;
+}
+
+static int snd_hrtimer_start(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ atomic_set(&stime->running, 0);
+ hrtimer_cancel(&stime->hrt);
+ hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
+ HRTIMER_MODE_REL);
+ atomic_set(&stime->running, 1);
+ return 0;
+}
+
+static int snd_hrtimer_stop(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+ atomic_set(&stime->running, 0);
+ return 0;
+}
+
+static struct snd_timer_hardware hrtimer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET,
+ .open = snd_hrtimer_open,
+ .close = snd_hrtimer_close,
+ .start = snd_hrtimer_start,
+ .stop = snd_hrtimer_stop,
+};
+
+/*
+ * entry functions
+ */
+
+static struct snd_timer *mytimer;
+
+static int __init snd_hrtimer_init(void)
+{
+ struct snd_timer *timer;
+ struct timespec tp;
+ int err;
+
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec > 0 || !tp.tv_nsec) {
+ pr_err("snd-hrtimer: Invalid resolution %u.%09u",
+ (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
+ return -EINVAL;
+ }
+ resolution = tp.tv_nsec;
+
+ /* Create a new timer and set up the fields */
+ err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
+ &timer);
+ if (err < 0)
+ return err;
+
+ timer->module = THIS_MODULE;
+ strcpy(timer->name, "HR timer");
+ timer->hw = hrtimer_hw;
+ timer->hw.resolution = resolution;
+ timer->hw.ticks = NANO_SEC / resolution;
+
+ err = snd_timer_global_register(timer);
+ if (err < 0) {
+ snd_timer_global_free(timer);
+ return err;
+ }
+ mytimer = timer; /* remember this */
+
+ return 0;
+}
+
+static void __exit snd_hrtimer_exit(void)
+{
+ if (mytimer) {
+ snd_timer_global_free(mytimer);
+ mytimer = NULL;
+ }
+}
+
+module_init(snd_hrtimer_init);
+module_exit(snd_hrtimer_exit);
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 46b47689362..69459e5f712 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -1,6 +1,6 @@
/*
* Hardware dependent layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,20 +19,19 @@
*
*/
-#include <sound/driver.h>
#include <linux/major.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#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>
#include <sound/hwdep.h>
#include <sound/info.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Hardware dependent layer");
MODULE_LICENSE("GPL");
@@ -47,14 +46,11 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device);
static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_hwdep *hwdep;
- list_for_each(p, &snd_hwdep_devices) {
- hwdep = list_entry(p, struct snd_hwdep, list);
+ list_for_each_entry(hwdep, &snd_hwdep_devices, list)
if (hwdep->card == card && hwdep->device == device)
return hwdep;
- }
return NULL;
}
@@ -104,11 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
if (hw == NULL)
return -ENODEV;
- if (!hw->ops.open)
- return -ENXIO;
-
- if (!try_module_get(hw->card->module))
+ if (!try_module_get(hw->card->module)) {
+ snd_card_unref(hw->card);
return -EFAULT;
+ }
init_waitqueue_entry(&wait, current);
add_wait_queue(&hw->open_wait, &wait);
@@ -118,6 +113,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
err = -EBUSY;
break;
}
+ if (!hw->ops.open) {
+ err = 0;
+ break;
+ }
err = hw->ops.open(hw, file);
if (err >= 0)
break;
@@ -132,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;
@@ -151,23 +154,25 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
if (err < 0)
module_put(hw->card->module);
+ snd_card_unref(hw->card);
return err;
}
static int snd_hwdep_release(struct inode *inode, struct file * file)
{
- int err = -ENXIO;
+ int err = 0;
struct snd_hwdep *hw = file->private_data;
struct module *mod = hw->card->module;
+
mutex_lock(&hw->open_mutex);
- if (hw->ops.release) {
+ if (hw->ops.release)
err = hw->ops.release(hw, file);
- wake_up(&hw->open_wait);
- }
if (hw->used > 0)
hw->used--;
- snd_card_file_remove(hw->card, file);
mutex_unlock(&hw->open_mutex);
+ wake_up(&hw->open_wait);
+
+ snd_card_file_remove(hw->card, file);
module_put(mod);
return err;
}
@@ -275,7 +280,14 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
mutex_lock(&register_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;
@@ -319,7 +331,7 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
*/
-static struct file_operations snd_hwdep_f_ops =
+static const struct file_operations snd_hwdep_f_ops =
{
.owner = THIS_MODULE,
.llseek = snd_hwdep_llseek,
@@ -344,7 +356,7 @@ static 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)
@@ -357,12 +369,13 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_hwdep_dev_disconnect,
};
- snd_assert(rhwdep != NULL, return -EINVAL);
- *rhwdep = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rhwdep)
+ *rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
if (hwdep == NULL) {
- snd_printk(KERN_ERR "hwdep: cannot allocate\n");
+ dev_err(card->dev, "hwdep: cannot allocate\n");
return -ENOMEM;
}
hwdep->card = card;
@@ -378,13 +391,16 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
}
init_waitqueue_head(&hwdep->open_wait);
mutex_init(&hwdep->open_mutex);
- *rhwdep = hwdep;
+ if (rhwdep)
+ *rhwdep = hwdep;
return 0;
}
+EXPORT_SYMBOL(snd_hwdep_new);
static int snd_hwdep_free(struct snd_hwdep *hwdep)
{
- snd_assert(hwdep != NULL, return -ENXIO);
+ if (!hwdep)
+ return 0;
if (hwdep->private_free)
hwdep->private_free(hwdep);
kfree(hwdep);
@@ -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(&register_mutex);
- if (snd_hwdep_search(hwdep->card, hwdep->device)) {
+ if (snd_hwdep_search(card, hwdep->device)) {
mutex_unlock(&register_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(&register_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;
}
@@ -444,18 +484,22 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
{
struct snd_hwdep *hwdep = device->device_data;
- snd_assert(hwdep != NULL, return -ENXIO);
+ if (snd_BUG_ON(!hwdep))
+ return -ENXIO;
mutex_lock(&register_mutex);
if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
mutex_unlock(&register_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(&register_mutex);
return 0;
}
@@ -468,15 +512,12 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
static void snd_hwdep_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *p;
struct snd_hwdep *hwdep;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_hwdep_devices) {
- hwdep = list_entry(p, struct snd_hwdep, list);
+ list_for_each_entry(hwdep, &snd_hwdep_devices, list)
snd_iprintf(buffer, "%02i-%02i: %s\n",
hwdep->card->number, hwdep->device, hwdep->name);
- }
mutex_unlock(&register_mutex);
}
@@ -527,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 0b4aab3225e..051d55b0552 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -1,6 +1,6 @@
/*
* Information interface for ALSA driver
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,15 +19,16 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/time.h>
-#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
+#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
#include <stdarg.h>
@@ -88,12 +89,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
char *nbuf;
nsize = PAGE_ALIGN(nsize);
- nbuf = kmalloc(nsize, GFP_KERNEL);
+ nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO);
if (! nbuf)
return -ENOMEM;
- memcpy(nbuf, buffer->buffer, buffer->len);
- kfree(buffer->buffer);
buffer->buffer = nbuf;
buffer->len = nsize;
return 0;
@@ -106,9 +105,9 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
*
* Outputs the string on the procfs buffer just like printf().
*
- * Returns the size of output string.
+ * Return: The size of output string, or a negative error code.
*/
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
{
va_list args;
int len, res;
@@ -154,56 +153,48 @@ EXPORT_SYMBOL(snd_seq_root);
struct snd_info_entry *snd_oss_root;
#endif
-static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
-{
- de->owner = THIS_MODULE;
-}
-
-static void snd_remove_proc_entry(struct proc_dir_entry *parent,
- struct proc_dir_entry *de)
-{
- if (de)
- remove_proc_entry(de->name, parent);
-}
-
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
struct snd_info_entry *entry;
- loff_t ret;
+ loff_t ret = -EINVAL, size;
data = file->private_data;
entry = data->entry;
- lock_kernel();
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- switch (orig) {
- case SEEK_SET:
- file->f_pos = offset;
- ret = file->f_pos;
- goto out;
- case SEEK_CUR:
- file->f_pos += offset;
- ret = file->f_pos;
- goto out;
- case SEEK_END:
- default:
- ret = -EINVAL;
- goto out;
- }
+ mutex_lock(&entry->access);
+ if (entry->content == SNDRV_INFO_CONTENT_DATA &&
+ entry->c.ops->llseek) {
+ offset = entry->c.ops->llseek(entry,
+ data->file_private_data,
+ file, offset, orig);
+ goto out;
+ }
+ if (entry->content == SNDRV_INFO_CONTENT_DATA)
+ size = entry->size;
+ else
+ size = 0;
+ switch (orig) {
+ case SEEK_SET:
break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->llseek) {
- ret = entry->c.ops->llseek(entry,
- data->file_private_data,
- file, offset, orig);
+ case SEEK_CUR:
+ offset += file->f_pos;
+ break;
+ case SEEK_END:
+ if (!size)
goto out;
- }
+ offset += size;
break;
- }
- ret = -ENXIO;
-out:
- unlock_kernel();
+ default:
+ goto out;
+ }
+ if (offset < 0)
+ goto out;
+ if (size && offset > size)
+ offset = size;
+ file->f_pos = offset;
+ ret = offset;
+ out:
+ mutex_unlock(&entry->access);
return ret;
}
@@ -217,7 +208,8 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
loff_t pos;
data = file->private_data;
- snd_assert(data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!data))
+ return -ENXIO;
pos = *offset;
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
return -EIO;
@@ -237,10 +229,15 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
return -EFAULT;
break;
case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->read)
+ if (pos >= entry->size)
+ return 0;
+ if (entry->c.ops->read) {
+ size = entry->size - pos;
+ size = min(count, size);
size = entry->c.ops->read(entry,
data->file_private_data,
- file, buffer, count, pos);
+ file, buffer, size, pos);
+ }
break;
}
if ((ssize_t) size > 0)
@@ -258,7 +255,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
loff_t pos;
data = file->private_data;
- snd_assert(data != NULL, return -ENXIO);
+ if (snd_BUG_ON(!data))
+ return -ENXIO;
entry = data->entry;
pos = *offset;
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
@@ -286,10 +284,13 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
size = count;
break;
case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->write)
+ if (entry->c.ops->write && count > 0) {
+ size_t maxsize = entry->size - pos;
+ count = min(count, maxsize);
size = entry->c.ops->write(entry,
data->file_private_data,
file, buffer, count, pos);
+ }
break;
}
if ((ssize_t) size > 0)
@@ -302,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;
@@ -345,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;
}
@@ -419,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);
@@ -488,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_dentry->d_inode;
+ struct inode *inode = file_inode(file);
struct snd_info_private_data *data;
struct snd_info_entry *entry;
@@ -507,7 +511,7 @@ static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
return -ENXIO;
}
-static struct file_operations snd_info_entry_operations =
+static const struct file_operations snd_info_entry_operations =
{
.owner = THIS_MODULE,
.llseek = snd_info_entry_llseek,
@@ -520,32 +524,11 @@ static struct file_operations snd_info_entry_operations =
.release = snd_info_entry_release,
};
-/**
- * snd_create_proc_entry - create a procfs entry
- * @name: the name of the proc file
- * @mode: the file permission bits, S_Ixxx
- * @parent: the parent proc-directory entry
- *
- * Creates a new proc file entry with the given name and permission
- * on the given directory.
- *
- * Returns the pointer of new instance or NULL on failure.
- */
-static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
- struct proc_dir_entry *parent)
-{
- struct proc_dir_entry *p;
- p = create_proc_entry(name, mode, parent);
- if (p)
- snd_info_entry_prepare(p);
- return p;
-}
-
int __init snd_info_init(void)
{
struct proc_dir_entry *p;
- p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
+ p = proc_mkdir("asound", NULL);
if (p == NULL)
return -ENOMEM;
snd_proc_root = p;
@@ -562,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)
@@ -589,13 +572,13 @@ int __exit snd_info_done(void)
snd_minor_info_done();
snd_info_version_done();
if (snd_proc_root) {
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
snd_info_free_entry(snd_seq_root);
#endif
#ifdef CONFIG_SND_OSSEMUL
snd_info_free_entry(snd_oss_root);
#endif
- snd_remove_proc_entry(&proc_root, snd_proc_root);
+ proc_remove(snd_proc_root);
}
return 0;
}
@@ -614,7 +597,8 @@ int snd_info_card_create(struct snd_card *card)
char str[8];
struct snd_info_entry *entry;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
sprintf(str, "card%i", card->number);
if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
@@ -636,7 +620,8 @@ int snd_info_card_register(struct snd_card *card)
{
struct proc_dir_entry *p;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
if (!strcmp(card->id, card->proc_root->name))
return 0;
@@ -649,17 +634,33 @@ int snd_info_card_register(struct snd_card *card)
}
/*
- * de-register the card proc file
- * called from init.c
+ * called on card->id change
*/
-void snd_info_card_disconnect(struct snd_card *card)
+void snd_info_card_id_change(struct snd_card *card)
{
- snd_assert(card != NULL, return);
mutex_lock(&info_mutex);
if (card->proc_root_link) {
- snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+ proc_remove(card->proc_root_link);
card->proc_root_link = NULL;
}
+ if (strcmp(card->id, card->proc_root->name))
+ card->proc_root_link = proc_symlink(card->id,
+ snd_proc_root,
+ card->proc_root->name);
+ mutex_unlock(&info_mutex);
+}
+
+/*
+ * de-register the card proc file
+ * called from init.c
+ */
+void snd_info_card_disconnect(struct snd_card *card)
+{
+ if (!card)
+ return;
+ mutex_lock(&info_mutex);
+ proc_remove(card->proc_root_link);
+ card->proc_root_link = NULL;
if (card->proc_root)
snd_info_disconnect(card->proc_root);
mutex_unlock(&info_mutex);
@@ -671,7 +672,8 @@ void snd_info_card_disconnect(struct snd_card *card)
*/
int snd_info_card_free(struct snd_card *card)
{
- snd_assert(card != NULL, return -ENXIO);
+ if (!card)
+ return 0;
snd_info_free_entry(card->proc_root);
card->proc_root = NULL;
return 0;
@@ -686,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;
}
@@ -727,10 +724,10 @@ EXPORT_SYMBOL(snd_info_get_line);
* Parses the original string and copy a token to the given
* string buffer.
*
- * Returns the updated pointer of the original string so that
+ * Return: The updated pointer of the original string so that
* it can be used for the next call.
*/
-char *snd_info_get_str(char *dest, char *src, int len)
+const char *snd_info_get_str(char *dest, const char *src, int len)
{
int c;
@@ -766,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)
{
@@ -795,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,
@@ -819,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,
@@ -849,8 +846,8 @@ static void snd_info_disconnect(struct snd_info_entry *entry)
return;
list_del_init(&entry->list);
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_assert(root, return);
- snd_remove_proc_entry(root, entry->p);
+ snd_BUG_ON(!root);
+ proc_remove(entry->p);
entry->p = NULL;
}
@@ -885,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)
@@ -941,25 +938,31 @@ EXPORT_SYMBOL(snd_info_free_entry);
*
* Registers the proc info entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_info_register(struct snd_info_entry * entry)
{
struct proc_dir_entry *root, *p = NULL;
- snd_assert(entry != NULL, return -ENXIO);
+ if (snd_BUG_ON(!entry))
+ return -ENXIO;
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
mutex_lock(&info_mutex);
- p = snd_create_proc_entry(entry->name, entry->mode, root);
- if (!p) {
- mutex_unlock(&info_mutex);
- return -ENOMEM;
+ if (S_ISDIR(entry->mode)) {
+ p = proc_mkdir_mode(entry->name, entry->mode, root);
+ if (!p) {
+ mutex_unlock(&info_mutex);
+ return -ENOMEM;
+ }
+ } else {
+ p = proc_create_data(entry->name, entry->mode, root,
+ &snd_info_entry_operations, entry);
+ if (!p) {
+ mutex_unlock(&info_mutex);
+ return -ENOMEM;
+ }
+ proc_set_size(p, entry->size);
}
- p->owner = entry->module;
- if (!S_ISDIR(entry->mode))
- p->proc_fops = &snd_info_entry_operations;
- p->size = entry->size;
- p->data = entry;
entry->p = p;
if (entry->parent)
list_add_tail(&entry->list, &entry->parent->children);
@@ -978,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 a444bfe2cf7..83c29dbff9c 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -1,6 +1,6 @@
/*
* Information interface for ALSA driver
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,14 +19,13 @@
*
*/
-#include <sound/driver.h>
#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>
@@ -44,8 +43,10 @@ int snd_oss_info_register(int dev, int num, char *string)
{
char *x;
- snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
- snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
+ if (snd_BUG_ON(dev < 0 || dev >= SNDRV_OSS_INFO_DEV_COUNT))
+ return -ENXIO;
+ if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
+ return -ENXIO;
mutex_lock(&strings);
if (string == NULL) {
if ((x = snd_sndstat_strings[num][dev]) != NULL) {
@@ -66,8 +67,6 @@ int snd_oss_info_register(int dev, int num, char *string)
EXPORT_SYMBOL(snd_oss_info_register);
-extern void snd_card_info_read_oss(struct snd_info_buffer *buffer);
-
static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
{
int idx, ok = -1;
@@ -94,7 +93,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
static void snd_sndstat_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+ snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA emulation code)\n");
snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
init_utsname()->sysname,
init_utsname()->nodename,
diff --git a/sound/core/init.c b/sound/core/init.c
index 6152a7554df..7bdfd19e24a 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1,6 +1,6 @@
/*
* Initialization routines
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,32 +19,83 @@
*
*/
-#include <sound/driver.h>
#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/pci.h>
#include <linux/pm.h>
+#include <linux/completion.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
+/* monitor files for graceful shutdown (hotplug) */
+struct snd_monitor_file {
+ struct file *file;
+ const struct file_operations *disconnected_f_op;
+ struct list_head shutdown_list; /* still need to shutdown */
+ struct list_head list; /* link of monitor files */
+};
+
static DEFINE_SPINLOCK(shutdown_lock);
static LIST_HEAD(shutdown_files);
-static struct file_operations snd_shutdown_f_ops;
+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);
static DEFINE_MUTEX(snd_card_mutex);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+static char *slots[SNDRV_CARDS];
+module_param_array(slots, charp, NULL, 0444);
+MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
+
+/* return non-zero if the given index is reserved for the given
+ * module via slots option
+ */
+static int module_slot_match(struct module *module, int idx)
+{
+ int match = 1;
+#ifdef MODULE
+ const char *s1, *s2;
+
+ if (!module || !*module->name || !slots[idx])
+ return 0;
+
+ s1 = module->name;
+ s2 = slots[idx];
+ if (*s2 == '!') {
+ match = 0; /* negative match */
+ s2++;
+ }
+ /* compare module name strings
+ * hyphens are handled as equivalent with underscore
+ */
+ for (;;) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if (c1 == '-')
+ c1 = '_';
+ if (c2 == '-')
+ c2 = '_';
+ if (c1 != c2)
+ return !match;
+ if (!c1)
+ break;
+ }
+#endif /* MODULE */
+ return match;
+}
+
+#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
@@ -62,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;
@@ -81,93 +132,146 @@ static inline int init_info_for_card(struct snd_card *card)
#define init_info_for_card(card)
#endif
+static int check_empty_slot(struct module *module, int slot)
+{
+ return !slots[slot] || !*slots[slot];
+}
+
+/* return an empty slot number (>= 0) found in the given bitmask @mask.
+ * @mask == -1 == 0xffffffff means: take any free slot up to 32
+ * when no slot is available, return the original @mask as is.
+ */
+static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
+ struct module *module)
+{
+ int slot;
+
+ for (slot = 0; slot < SNDRV_CARDS; slot++) {
+ if (slot < 32 && !(mask & (1U << slot)))
+ continue;
+ if (!test_bit(slot, snd_cards_lock)) {
+ if (check(module, slot))
+ return slot; /* found */
+ }
+ }
+ return mask; /* unchanged */
+}
+
+static int snd_card_do_free(struct snd_card *card);
+static const struct attribute_group *card_dev_attr_groups[];
+
+static void release_card_device(struct device *dev)
+{
+ snd_card_do_free(dev_to_snd_card(dev));
+}
+
/**
* snd_card_new - create and initialize a soundcard structure
+ * @parent: the parent device object
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
+ * @card_ret: the pointer to store the created card instance
*
* Creates and initializes a soundcard structure.
*
- * Returns kmallocated snd_card structure. Creates the ALSA control interface
- * (which is blocked until snd_card_register function is called).
+ * The function allocates snd_card instance via kzalloc with the given
+ * space for the driver to use freely. The allocated struct is stored
+ * in the given card_ret pointer.
+ *
+ * Return: Zero if successful or a negative error code.
*/
-struct snd_card *snd_card_new(int idx, const char *xid,
- struct module *module, int extra_size)
+int snd_card_new(struct device *parent, int idx, const char *xid,
+ struct module *module, int extra_size,
+ struct snd_card **card_ret)
{
struct snd_card *card;
int err;
+ if (snd_BUG_ON(!card_ret))
+ return -EINVAL;
+ *card_ret = NULL;
+
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
- if (card == NULL)
- return NULL;
- if (xid) {
- if (!snd_info_check_reserved_words(xid))
- goto __error;
+ if (!card)
+ return -ENOMEM;
+ if (extra_size > 0)
+ card->private_data = (char *)card + sizeof(struct snd_card);
+ if (xid)
strlcpy(card->id, xid, sizeof(card->id));
- }
err = 0;
mutex_lock(&snd_card_mutex);
- if (idx < 0) {
- int idx2;
- for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
- if (~snd_cards_lock & idx & 1<<idx2) {
- idx = idx2;
- if (idx >= snd_ecards_limit)
- snd_ecards_limit = idx + 1;
- break;
- }
- } else if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
- err = -ENODEV; /* invalid */
- } else if (idx < SNDRV_CARDS)
- snd_ecards_limit = idx + 1; /* increase the limit */
- else
+ 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;
- if (idx < 0 || err < 0) {
+ else if (idx < snd_ecards_limit) {
+ if (test_bit(idx, snd_cards_lock))
+ err = -EBUSY; /* invalid */
+ } else if (idx >= SNDRV_CARDS)
+ err = -ENODEV;
+ if (err < 0) {
mutex_unlock(&snd_card_mutex);
- snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
- goto __error;
+ dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
+ idx, snd_ecards_limit - 1, err);
+ kfree(card);
+ return err;
}
- snd_cards_lock |= 1 << idx; /* lock it */
+ set_bit(idx, snd_cards_lock); /* lock it */
+ if (idx >= snd_ecards_limit)
+ snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
+ card->dev = parent;
card->number = idx;
card->module = module;
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
+ mutex_init(&card->user_ctl_lock);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
- init_waitqueue_head(&card->shutdown_sleep);
+ INIT_LIST_HEAD(&card->files_list);
#ifdef CONFIG_PM
mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
#endif
+
+ device_initialize(&card->card_dev);
+ card->card_dev.parent = parent;
+ card->card_dev.class = sound_class;
+ card->card_dev.release = release_card_device;
+ card->card_dev.groups = card_dev_attr_groups;
+ err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
+ if (err < 0)
+ goto __error;
+
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
- if ((err = snd_ctl_create(card)) < 0) {
- snd_printd("unable to register control minors\n");
+ err = snd_ctl_create(card);
+ if (err < 0) {
+ dev_err(parent, "unable to register control minors\n");
goto __error;
}
- if ((err = snd_info_card_create(card)) < 0) {
- snd_printd("unable to create card info\n");
+ err = snd_info_card_create(card);
+ if (err < 0) {
+ dev_err(parent, "unable to create card info\n");
goto __error_ctl;
}
- if (extra_size > 0)
- card->private_data = (char *)card + sizeof(struct snd_card);
- return card;
+ *card_ret = card;
+ return 0;
__error_ctl:
- snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
+ snd_device_free_all(card);
__error:
- kfree(card);
- return NULL;
+ put_device(&card->card_dev);
+ return err;
}
-
EXPORT_SYMBOL(snd_card_new);
/* return non-zero if a card is already locked */
@@ -176,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;
}
@@ -206,15 +310,19 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
list_for_each_entry(_df, &shutdown_files, shutdown_list) {
if (_df->file == file) {
df = _df;
+ list_del_init(&df->shutdown_list);
break;
}
}
spin_unlock(&shutdown_lock);
- if (likely(df))
+ if (likely(df)) {
+ if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
+ df->disconnected_f_op->fasync(-1, file, 0);
return df->disconnected_f_op->release(inode, file);
+ }
- panic("%s(%p, %p) failed!", __FUNCTION__, inode, file);
+ panic("%s(%p, %p) failed!", __func__, inode, file);
}
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
@@ -238,7 +346,7 @@ static int snd_disconnect_fasync(int fd, struct file *file, int on)
return -ENODEV;
}
-static struct file_operations snd_shutdown_f_ops =
+static const struct file_operations snd_shutdown_f_ops =
{
.owner = THIS_MODULE,
.llseek = snd_disconnect_llseek,
@@ -260,7 +368,7 @@ static 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).
@@ -268,9 +376,11 @@ static struct file_operations snd_shutdown_f_ops =
int snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
- struct file *file;
int err;
+ if (!card)
+ return -EINVAL;
+
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
@@ -282,15 +392,13 @@ int snd_card_disconnect(struct snd_card *card)
/* phase 1: disable fops (user space) operations for ALSA API */
mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
+ clear_bit(card->number, snd_cards_lock);
mutex_unlock(&snd_card_mutex);
/* phase 2: replace file->f_op with special dummy operations */
spin_lock(&card->files_lock);
- mfile = card->files;
- while (mfile) {
- file = mfile->file;
-
+ list_for_each_entry(mfile, &card->files_list, list) {
/* it's critical part, use endless loop */
/* we have no room to fail */
mfile->disconnected_f_op = mfile->file->f_op;
@@ -299,17 +407,15 @@ int snd_card_disconnect(struct snd_card *card)
list_add(&mfile->shutdown_list, &shutdown_files);
spin_unlock(&shutdown_lock);
- fops_get(&snd_shutdown_f_ops);
mfile->file->f_op = &snd_shutdown_f_ops;
-
- mfile = mfile->next;
+ fops_get(mfile->file->f_op);
}
spin_unlock(&card->files_lock);
/* phase 3: notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
#endif
@@ -317,9 +423,16 @@ int snd_card_disconnect(struct snd_card *card)
/* notify all devices that we are disconnected */
err = snd_device_disconnect_all(card);
if (err < 0)
- snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+ dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number);
snd_info_card_disconnect(card);
+ if (card->registered) {
+ device_del(&card->card_dev);
+ card->registered = false;
+ }
+#ifdef CONFIG_PM
+ wake_up(&card->power_sleep);
+#endif
return 0;
}
@@ -333,154 +446,235 @@ EXPORT_SYMBOL(snd_card_disconnect);
* devices automatically. That is, you don't have to release the devices
* by yourself.
*
- * Returns zero. Frees all associated devices and frees the control
+ * Return: Zero. Frees all associated devices and frees the control
* interface associated to given soundcard.
*/
static int snd_card_do_free(struct snd_card *card)
{
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
#endif
- if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (pre)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (normal)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (post)\n");
- /* Fatal, but this situation should never occur */
- }
+ snd_device_free_all(card);
if (card->private_free)
card->private_free(card);
snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) {
- snd_printk(KERN_WARNING "unable to free card info\n");
+ dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
}
- if (card->dev)
- device_unregister(card->dev);
+ if (card->release_completion)
+ complete(card->release_completion);
kfree(card);
return 0;
}
-static int snd_card_free_prepare(struct snd_card *card)
-{
- if (card == NULL)
- return -EINVAL;
- (void) snd_card_disconnect(card);
- mutex_lock(&snd_card_mutex);
- snd_cards[card->number] = NULL;
- snd_cards_lock &= ~(1 << card->number);
- mutex_unlock(&snd_card_mutex);
-#ifdef CONFIG_PM
- wake_up(&card->power_sleep);
-#endif
- return 0;
-}
-
int snd_card_free_when_closed(struct snd_card *card)
{
- int free_now = 0;
- int ret = snd_card_free_prepare(card);
+ int ret = snd_card_disconnect(card);
if (ret)
return ret;
-
- spin_lock(&card->files_lock);
- if (card->files == NULL)
- free_now = 1;
- else
- card->free_on_last_close = 1;
- spin_unlock(&card->files_lock);
-
- if (free_now)
- snd_card_do_free(card);
+ put_device(&card->card_dev);
return 0;
}
-
EXPORT_SYMBOL(snd_card_free_when_closed);
int snd_card_free(struct snd_card *card)
{
- int ret = snd_card_free_prepare(card);
+ struct completion released;
+ int ret;
+
+ init_completion(&released);
+ card->release_completion = &released;
+ ret = snd_card_free_when_closed(card);
if (ret)
return ret;
-
/* wait, until all devices are ready for the free operation */
- wait_event(card->shutdown_sleep, card->files == NULL);
- snd_card_do_free(card);
+ wait_for_completion(&released);
return 0;
}
-
EXPORT_SYMBOL(snd_card_free);
-static void choose_default_id(struct snd_card *card)
+/* retrieve the last word of shortname or longname */
+static const char *retrieve_id_from_card_name(const char *name)
{
- int i, len, idx_flag = 0, loops = SNDRV_CARDS;
- char *id, *spos;
-
- id = spos = card->shortname;
- while (*id != '\0') {
- if (*id == ' ')
- spos = id + 1;
- id++;
- }
- id = card->id;
- while (*spos != '\0' && !isalnum(*spos))
- spos++;
- if (isdigit(*spos))
- *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
- while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
- if (isalnum(*spos))
- *id++ = *spos;
- spos++;
+ const char *spos = name;
+
+ while (*name) {
+ if (isspace(*name) && isalnum(name[1]))
+ spos = name + 1;
+ name++;
}
- *id = '\0';
+ return spos;
+}
- id = card->id;
+/* 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 (*id == '\0')
- strcpy(id, "default");
+ copy_valid_id_string(card, src, nid);
+ id = card->id;
- while (1) {
- if (loops-- == 0) {
- snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
- strcpy(card->id, card->proc_root->name);
- return;
- }
- if (!snd_info_check_reserved_words(id))
- goto __change;
- for (i = 0; i < snd_ecards_limit; i++) {
- if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
- goto __change;
- }
- break;
-
- __change:
- len = strlen(id);
- if (idx_flag) {
- if (id[len-1] != '9')
- id[len-1]++;
- else
- id[len-1] = 'A';
- } else if ((size_t)len <= sizeof(card->id) - 3) {
- strcat(id, "_1");
- idx_flag++;
- } else {
- spos = id + len - 2;
- if ((size_t)len <= sizeof(card->id) - 2)
- spos++;
- *spos++ = '_';
- *spos++ = '1';
- *spos++ = '\0';
- idx_flag++;
- }
+ again:
+ /* use "Default" for obviously invalid strings
+ * ("card" conflicts with proc directories)
+ */
+ if (!*id || !strncmp(id, "card", 4)) {
+ strcpy(id, "Default");
+ is_default = true;
+ }
+
+ len = strlen(id);
+ for (loops = 0; loops < SNDRV_CARDS; loops++) {
+ char *spos;
+ char sfxstr[5]; /* "_012" */
+ int sfxlen;
+
+ if (card_id_ok(card, id))
+ return; /* OK */
+
+ /* Add _XYZ suffix */
+ sprintf(sfxstr, "_%X", loops + 1);
+ sfxlen = strlen(sfxstr);
+ if (len + sfxlen >= sizeof(card->id))
+ spos = id + sizeof(card->id) - sfxlen - 1;
+ else
+ spos = id + len;
+ strcpy(spos, sfxstr);
+ }
+ /* fallback to the default id */
+ if (!is_default) {
+ *id = 0;
+ goto again;
+ }
+ /* last resort... */
+ dev_err(card->dev, "unable to set card id (%s)\n", id);
+ if (card->proc_root->name)
+ strlcpy(card->id, card->proc_root->name, sizeof(card->id));
+}
+
+/**
+ * snd_card_set_id - set card identification name
+ * @card: soundcard structure
+ * @nid: new identification string
+ *
+ * This function sets the card identification and checks for name
+ * collisions.
+ */
+void snd_card_set_id(struct snd_card *card, const char *nid)
+{
+ /* check if user specified own card->id */
+ if (card->id[0] != '\0')
+ return;
+ mutex_lock(&snd_card_mutex);
+ snd_card_set_id_no_lock(card, nid, nid);
+ mutex_unlock(&snd_card_mutex);
+}
+EXPORT_SYMBOL(snd_card_set_id);
+
+static ssize_t
+card_id_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return snprintf(buf, PAGE_SIZE, "%s\n", card->id);
+}
+
+static ssize_t
+card_id_store_attr(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ char buf1[sizeof(card->id)];
+ size_t copy = count > sizeof(card->id) - 1 ?
+ sizeof(card->id) - 1 : count;
+ size_t idx;
+ int c;
+
+ for (idx = 0; idx < copy; idx++) {
+ c = buf[idx];
+ if (!isalnum(c) && c != '_' && c != '-')
+ return -EINVAL;
+ }
+ memcpy(buf1, buf, copy);
+ buf1[copy] = '\0';
+ mutex_lock(&snd_card_mutex);
+ if (!card_id_ok(NULL, buf1)) {
+ mutex_unlock(&snd_card_mutex);
+ return -EEXIST;
}
+ strcpy(card->id, buf1);
+ snd_info_card_id_change(card);
+ mutex_unlock(&snd_card_mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
+
+static ssize_t
+card_number_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return snprintf(buf, PAGE_SIZE, "%i\n", card->number);
}
+static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+
+static struct attribute *card_dev_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_number.attr,
+ NULL
+};
+
+static struct attribute_group card_dev_attr_group = {
+ .attrs = card_dev_attrs,
+};
+
+static const struct attribute_group *card_dev_attr_groups[] = {
+ &card_dev_attr_group,
+ NULL
+};
+
/**
* snd_card_register - register the soundcard
* @card: soundcard structure
@@ -490,19 +684,22 @@ static void choose_default_id(struct snd_card *card)
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
- * Returns zero otherwise a negative error code if the registrain failed.
+ * Return: Zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
int err;
- snd_assert(card != NULL, return -EINVAL);
- if (!card->dev) {
- card->dev = device_create(sound_class, card->parent, 0,
- "card%i", card->number);
- if (IS_ERR(card->dev))
- card->dev = NULL;
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
+
+ if (!card->registered) {
+ err = device_add(&card->card_dev);
+ if (err < 0)
+ return err;
+ card->registered = true;
}
+
if ((err = snd_device_register_all(card)) < 0)
return err;
mutex_lock(&snd_card_mutex);
@@ -511,12 +708,22 @@ int snd_card_register(struct snd_card *card)
mutex_unlock(&snd_card_mutex);
return 0;
}
- if (card->id[0] == '\0')
- choose_default_id(card);
+ if (*card->id) {
+ /* make a unique id name from the given string */
+ char tmpid[sizeof(card->id)];
+ memcpy(tmpid, card->id, sizeof(card->id));
+ snd_card_set_id_no_lock(card, tmpid, tmpid);
+ } else {
+ /* create an id from either shortname or longname */
+ const char *src;
+ src = *card->shortname ? card->shortname : card->longname;
+ snd_card_set_id_no_lock(card, src,
+ retrieve_id_from_card_name(src));
+ }
snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
@@ -639,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)
@@ -673,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)
{
@@ -684,15 +891,15 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
return -ENOMEM;
mfile->file = file;
mfile->disconnected_f_op = NULL;
- mfile->next = NULL;
+ INIT_LIST_HEAD(&mfile->shutdown_list);
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
kfree(mfile);
return -ENODEV;
}
- mfile->next = card->files;
- card->files = mfile;
+ list_add(&mfile->list, &card->files_list);
+ get_device(&card->card_dev);
spin_unlock(&card->files_lock);
return 0;
}
@@ -710,45 +917,32 @@ EXPORT_SYMBOL(snd_card_file_add);
* called beforehand, it processes the pending release of
* resources.
*
- * Returns zero or a negative error code.
+ * Return: Zero or a negative error code.
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
- struct snd_monitor_file *mfile, *pfile = NULL;
- int last_close = 0;
+ struct snd_monitor_file *mfile, *found = NULL;
spin_lock(&card->files_lock);
- mfile = card->files;
- while (mfile) {
+ list_for_each_entry(mfile, &card->files_list, list) {
if (mfile->file == file) {
- if (pfile)
- pfile->next = mfile->next;
- else
- card->files = mfile->next;
+ list_del(&mfile->list);
+ spin_lock(&shutdown_lock);
+ list_del(&mfile->shutdown_list);
+ spin_unlock(&shutdown_lock);
+ if (mfile->disconnected_f_op)
+ fops_put(mfile->disconnected_f_op);
+ found = mfile;
break;
}
- pfile = mfile;
- mfile = mfile->next;
- }
- if (mfile && mfile->disconnected_f_op) {
- fops_put(mfile->disconnected_f_op);
- spin_lock(&shutdown_lock);
- list_del(&mfile->shutdown_list);
- spin_unlock(&shutdown_lock);
}
- if (card->files == NULL)
- last_close = 1;
spin_unlock(&card->files_lock);
- if (last_close) {
- wake_up(&card->shutdown_sleep);
- if (card->free_on_last_close)
- snd_card_do_free(card);
- }
- if (!mfile) {
- snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
+ if (!found) {
+ dev_err(card->dev, "card file remove problem (%p)\n", file);
return -ENOENT;
}
- kfree(mfile);
+ kfree(found);
+ put_device(&card->card_dev);
return 0;
}
@@ -762,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 d52398727f0..31e8544d7f2 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -1,6 +1,6 @@
/*
* ISA DMA support functions
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
-#include <sound/driver.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <asm/dma.h>
@@ -81,24 +81,32 @@ EXPORT_SYMBOL(snd_dma_disable);
* @dma: the dma number
* @size: the dma transfer size
*
- * Returns the current pointer in DMA tranfer buffer in bytes
+ * Return: The current pointer in DMA transfer buffer in bytes.
*/
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
unsigned long flags;
- unsigned int result;
+ unsigned int result, result1;
flags = claim_dma_lock();
clear_dma_ff(dma);
if (!isa_dma_bridge_buggy)
disable_dma(dma);
result = get_dma_residue(dma);
+ /*
+ * HACK - read the counter again and choose higher value in order to
+ * avoid reading during counter lower byte roll over if the
+ * isa_dma_bridge_buggy is set.
+ */
+ result1 = get_dma_residue(dma);
if (!isa_dma_bridge_buggy)
enable_dma(dma);
release_dma_lock(flags);
+ if (unlikely(result < result1))
+ result = result1;
#ifdef CONFIG_SND_DEBUG
if (result > size)
- snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
+ pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
#endif
if (result >= size || result == 0)
return 0;
diff --git a/sound/core/jack.c b/sound/core/jack.c
new file mode 100644
index 00000000000..8658578eb58
--- /dev/null
+++ b/sound/core/jack.c
@@ -0,0 +1,260 @@
+/*
+ * Jack abstraction layer
+ *
+ * Copyright 2008 Wolfson Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/core.h>
+
+static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
+ SW_HEADPHONE_INSERT,
+ SW_MICROPHONE_INSERT,
+ SW_LINEOUT_INSERT,
+ SW_JACK_PHYSICAL_INSERT,
+ SW_VIDEOOUT_INSERT,
+ SW_LINEIN_INSERT,
+};
+
+static int snd_jack_dev_disconnect(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+
+ if (!jack->input_dev)
+ return 0;
+
+ /* If the input device is registered with the input subsystem
+ * then we need to use a different deallocator. */
+ if (jack->registered)
+ input_unregister_device(jack->input_dev);
+ else
+ input_free_device(jack->input_dev);
+ jack->input_dev = NULL;
+ return 0;
+}
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+
+ if (jack->private_free)
+ jack->private_free(jack);
+
+ snd_jack_dev_disconnect(device);
+
+ kfree(jack->id);
+ kfree(jack);
+
+ return 0;
+}
+
+static int snd_jack_dev_register(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+ struct snd_card *card = device->card;
+ int err, i;
+
+ snprintf(jack->name, sizeof(jack->name), "%s %s",
+ card->shortname, jack->id);
+ jack->input_dev->name = jack->name;
+
+ /* Default to the sound card device. */
+ if (!jack->input_dev->dev.parent)
+ jack->input_dev->dev.parent = snd_card_get_device_link(card);
+
+ /* Add capabilities for any keys that are enabled */
+ for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
+ int testbit = SND_JACK_BTN_0 >> i;
+
+ if (!(jack->type & testbit))
+ continue;
+
+ if (!jack->key[i])
+ jack->key[i] = BTN_0 + i;
+
+ input_set_capability(jack->input_dev, EV_KEY, jack->key[i]);
+ }
+
+ err = input_register_device(jack->input_dev);
+ if (err == 0)
+ jack->registered = 1;
+
+ return err;
+}
+
+/**
+ * snd_jack_new - Create a new jack
+ * @card: the card instance
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jjack: Used to provide the allocated jack object to the caller.
+ *
+ * Creates a new jack object.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ * On success @jjack will be initialised.
+ */
+int snd_jack_new(struct snd_card *card, const char *id, int type,
+ struct snd_jack **jjack)
+{
+ struct snd_jack *jack;
+ int err;
+ int i;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_jack_dev_free,
+ .dev_register = snd_jack_dev_register,
+ .dev_disconnect = snd_jack_dev_disconnect,
+ };
+
+ jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
+ if (jack == NULL)
+ return -ENOMEM;
+
+ jack->id = kstrdup(id, GFP_KERNEL);
+
+ jack->input_dev = input_allocate_device();
+ if (jack->input_dev == NULL) {
+ err = -ENOMEM;
+ goto fail_input;
+ }
+
+ jack->input_dev->phys = "ALSA";
+
+ jack->type = type;
+
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
+ if (type & (1 << i))
+ input_set_capability(jack->input_dev, EV_SW,
+ jack_switch_types[i]);
+
+ err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
+ if (err < 0)
+ goto fail_input;
+
+ *jjack = jack;
+
+ return 0;
+
+fail_input:
+ input_free_device(jack->input_dev);
+ kfree(jack->id);
+ kfree(jack);
+ return err;
+}
+EXPORT_SYMBOL(snd_jack_new);
+
+/**
+ * snd_jack_set_parent - Set the parent device for a jack
+ *
+ * @jack: The jack to configure
+ * @parent: The device to set as parent for the jack.
+ *
+ * Set the parent for the jack devices in the device tree. This
+ * function is only valid prior to registration of the jack. If no
+ * parent is configured then the parent device will be the sound card.
+ */
+void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
+{
+ WARN_ON(jack->registered);
+
+ jack->input_dev->dev.parent = parent;
+}
+EXPORT_SYMBOL(snd_jack_set_parent);
+
+/**
+ * snd_jack_set_key - Set a key mapping on a jack
+ *
+ * @jack: The jack to configure
+ * @type: Jack report type for this key
+ * @keytype: Input layer key type to be reported
+ *
+ * Map a SND_JACK_BTN_ button type to an input layer key, allowing
+ * reporting of keys on accessories via the jack abstraction. If no
+ * mapping is provided but keys are enabled in the jack type then
+ * BTN_n numeric buttons will be reported.
+ *
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
+ * Note that this is intended to be use by simple devices with small
+ * numbers of keys that can be reported. It is also possible to
+ * access the input device directly - devices with complex input
+ * capabilities on accessories should consider doing this rather than
+ * using this abstraction.
+ *
+ * This function may only be called prior to registration of the jack.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
+ int keytype)
+{
+ int key = fls(SND_JACK_BTN_0) - fls(type);
+
+ WARN_ON(jack->registered);
+
+ if (!keytype || key >= ARRAY_SIZE(jack->key))
+ return -EINVAL;
+
+ jack->type |= type;
+ jack->key[key] = keytype;
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_jack_set_key);
+
+/**
+ * snd_jack_report - Report the current status of a jack
+ *
+ * @jack: The jack to report status for
+ * @status: The current status of the jack
+ */
+void snd_jack_report(struct snd_jack *jack, int status)
+{
+ int i;
+
+ if (!jack)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
+ int testbit = SND_JACK_BTN_0 >> i;
+
+ if (jack->type & testbit)
+ input_report_key(jack->input_dev, jack->key[i],
+ status & testbit);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
+ int testbit = 1 << i;
+ if (jack->type & testbit)
+ input_report_switch(jack->input_dev,
+ jack_switch_types[i],
+ status & testbit);
+ }
+
+ input_sync(jack->input_dev);
+}
+EXPORT_SYMBOL(snd_jack_report);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("Jack detection support for ALSA");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index bc0bd0910a6..082509eb805 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
*
* Generic memory allocators
@@ -21,124 +21,11 @@
*
*/
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mm.h>
-#include <asm/uaccess.h>
#include <linux/dma-mapping.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
+#include <linux/genalloc.h>
#include <sound/memalloc.h>
-#ifdef CONFIG_SBUS
-#include <asm/sbus.h>
-#endif
-
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Memory allocator for ALSA system.");
-MODULE_LICENSE("GPL");
-
-
-/*
- */
-
-void *snd_malloc_sgbuf_pages(struct device *device,
- size_t size, struct snd_dma_buffer *dmab,
- size_t *res_size);
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
-
-/*
- */
-
-static DEFINE_MUTEX(list_mutex);
-static LIST_HEAD(mem_list_head);
-
-/* buffer preservation list */
-struct snd_mem_list {
- struct snd_dma_buffer buffer;
- unsigned int id;
- struct list_head list;
-};
-
-/* id for pre-allocated buffers */
-#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
-
-#ifdef CONFIG_SND_DEBUG
-#define __ASTRING__(x) #x
-#define snd_assert(expr, args...) do {\
- if (!(expr)) {\
- printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
- args;\
- }\
-} while (0)
-#else
-#define snd_assert(expr, args...) /**/
-#endif
-
-/*
- * Hacks
- */
-
-#if defined(__i386__)
-/*
- * A hack to allocate large buffers via dma_alloc_coherent()
- *
- * since dma_alloc_coherent always tries GFP_DMA when the requested
- * pci memory region is below 32bit, it happens quite often that even
- * 2 order of pages cannot be allocated.
- *
- * so in the following, we allocate at first without dma_mask, so that
- * allocation will be done without GFP_DMA. if the area doesn't match
- * with the requested region, then realloate with the original dma_mask
- * again.
- *
- * Really, we want to move this type of thing into dma_alloc_coherent()
- * so dma_mask doesn't have to be messed with.
- */
-
-static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle,
- gfp_t flags)
-{
- void *ret;
- u64 dma_mask, coherent_dma_mask;
-
- if (dev == NULL || !dev->dma_mask)
- return dma_alloc_coherent(dev, size, dma_handle, flags);
- dma_mask = *dev->dma_mask;
- coherent_dma_mask = dev->coherent_dma_mask;
- *dev->dma_mask = 0xffffffff; /* do without masking */
- dev->coherent_dma_mask = 0xffffffff; /* do without masking */
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- *dev->dma_mask = dma_mask; /* restore */
- dev->coherent_dma_mask = coherent_dma_mask; /* restore */
- if (ret) {
- /* obtained address is out of range? */
- if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
- /* reallocate with the proper mask */
- dma_free_coherent(dev, size, ret, *dma_handle);
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- }
- } else {
- /* wish to success now with the proper mask... */
- if (dma_mask != 0xffffffffUL) {
- /* allocation with GFP_ATOMIC to avoid the long stall */
- flags &= ~GFP_KERNEL;
- flags |= GFP_ATOMIC;
- ret = dma_alloc_coherent(dev, size, dma_handle, flags);
- }
- }
- return ret;
-}
-
-/* redefine dma_alloc_coherent for some architectures */
-#undef dma_alloc_coherent
-#define dma_alloc_coherent snd_dma_hack_alloc_coherent
-
-#endif /* arch */
/*
*
@@ -146,18 +33,6 @@ static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
*
*/
-static long snd_allocated_pages; /* holding the number of allocated pages */
-
-static inline void inc_snd_pages(int order)
-{
- snd_allocated_pages += 1 << order;
-}
-
-static inline void dec_snd_pages(int order)
-{
- snd_allocated_pages -= 1 << order;
-}
-
/**
* snd_malloc_pages - allocate pages with the given size
* @size: the size to allocate in bytes
@@ -165,20 +40,19 @@ static inline void dec_snd_pages(int order)
*
* Allocates the physically contiguous pages with the given size.
*
- * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ * Return: The pointer of the buffer, or %NULL if no enough memory.
*/
void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
{
int pg;
- void *res;
- snd_assert(size > 0, return NULL);
- snd_assert(gfp_flags != 0, return NULL);
+ if (WARN_ON(!size))
+ return NULL;
+ if (WARN_ON(!gfp_flags))
+ return NULL;
gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
pg = get_order(size);
- if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
- inc_snd_pages(pg);
- return res;
+ return (void *) __get_free_pages(gfp_flags, pg);
}
/**
@@ -195,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);
}
@@ -205,25 +78,21 @@ void snd_free_pages(void *ptr, size_t size)
*
*/
+#ifdef CONFIG_HAS_DMA
/* allocate the coherent DMA pages */
static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
{
int pg;
- void *res;
gfp_t gfp_flags;
- snd_assert(size > 0, return NULL);
- snd_assert(dma != NULL, return NULL);
+ if (WARN_ON(!dma))
+ return NULL;
pg = get_order(size);
gfp_flags = GFP_KERNEL
| __GFP_COMP /* compound page lets parts be mapped */
| __GFP_NORETRY /* don't trigger OOM-killer */
| __GFP_NOWARN; /* no stack trace print - this call is non-critical */
- res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
- if (res != NULL)
- inc_snd_pages(pg);
-
- return res;
+ return dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
}
/* free the coherent DMA pages */
@@ -235,42 +104,50 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
if (ptr == NULL)
return;
pg = get_order(size);
- dec_snd_pages(pg);
dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
}
-#ifdef CONFIG_SBUS
-
-static void *snd_malloc_sbus_pages(struct device *dev, size_t size,
- dma_addr_t *dma_addr)
+#ifdef CONFIG_GENERIC_ALLOCATOR
+/**
+ * snd_malloc_dev_iram - allocate memory from on-chip internal ram
+ * @dmab: buffer allocation record to store the allocated data
+ * @size: number of bytes to allocate from the iram
+ *
+ * This function requires iram phandle provided via of_node
+ */
+static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
- void *res;
+ struct device *dev = dmab->dev.dev;
+ struct gen_pool *pool = NULL;
- snd_assert(size > 0, return NULL);
- snd_assert(dma_addr != NULL, return NULL);
- pg = get_order(size);
- res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
- if (res != NULL)
- inc_snd_pages(pg);
- return res;
-}
+ dmab->area = NULL;
+ dmab->addr = 0;
-static void snd_free_sbus_pages(struct device *dev, size_t size,
- void *ptr, dma_addr_t dma_addr)
-{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
+ if (dev->of_node)
+ pool = of_get_named_gen_pool(dev->of_node, "iram", 0);
- if (ptr == NULL)
+ if (!pool)
return;
- pg = get_order(size);
- dec_snd_pages(pg);
- sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+
+ /* Assign the pool into private_data field */
+ dmab->private_data = pool;
+
+ dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr);
}
-#endif /* CONFIG_SBUS */
+/**
+ * snd_free_dev_iram - free allocated specific memory from on-chip internal ram
+ * @dmab: buffer allocation record to store the allocated data
+ */
+static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
+{
+ struct gen_pool *pool = dmab->private_data;
+
+ if (pool && dmab->area)
+ gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
+}
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+#endif /* CONFIG_HAS_DMA */
/*
*
@@ -288,37 +165,49 @@ static void snd_free_sbus_pages(struct device *dev, size_t size,
*
* Calls the memory-allocator function for the corresponding
* buffer type.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
{
- snd_assert(size > 0, return -ENXIO);
- snd_assert(dmab != NULL, return -ENXIO);
+ if (WARN_ON(!size))
+ return -ENXIO;
+ if (WARN_ON(!dmab))
+ return -ENXIO;
dmab->dev.type = type;
dmab->dev.dev = device;
dmab->bytes = 0;
switch (type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
- dmab->area = snd_malloc_pages(size, (unsigned long)device);
+ dmab->area = snd_malloc_pages(size,
+ (__force gfp_t)(unsigned long)device);
dmab->addr = 0;
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
- break;
-#endif
+#ifdef CONFIG_HAS_DMA
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ case SNDRV_DMA_TYPE_DEV_IRAM:
+ snd_malloc_dev_iram(dmab, size);
+ if (dmab->area)
+ break;
+ /* Internal memory might have limited size and no enough space,
+ * so if we fail to malloc, try to fetch memory traditionally.
+ */
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+#endif /* CONFIG_GENERIC_ALLOCATOR */
case SNDRV_DMA_TYPE_DEV:
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
break;
+#endif
default:
- printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
+ pr_err("snd-malloc: invalid device type %d\n", type);
dmab->area = NULL;
dmab->addr = 0;
return -ENXIO;
@@ -340,24 +229,26 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
* buffer type. When no space is left, this function reduces the size and
* tries to allocate again. The size actually allocated is stored in
* res_size argument.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
{
int err;
- snd_assert(size > 0, return -ENXIO);
- snd_assert(dmab != NULL, return -ENXIO);
-
while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
+ size_t aligned_size;
if (err != -ENOMEM)
return err;
- size >>= 1;
if (size <= PAGE_SIZE)
return -ENOMEM;
+ aligned_size = PAGE_SIZE << get_order(size);
+ if (size != aligned_size)
+ size = aligned_size;
+ else
+ size >>= 1;
}
if (! dmab->area)
return -ENOMEM;
@@ -377,265 +268,26 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
case SNDRV_DMA_TYPE_CONTINUOUS:
snd_free_pages(dmab->area, dmab->bytes);
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+#ifdef CONFIG_HAS_DMA
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ case SNDRV_DMA_TYPE_DEV_IRAM:
+ snd_free_dev_iram(dmab);
break;
-#endif
+#endif /* CONFIG_GENERIC_ALLOCATOR */
case SNDRV_DMA_TYPE_DEV:
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
snd_free_sgbuf_pages(dmab);
break;
- 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 list_head *p;
- struct snd_mem_list *mem;
-
- snd_assert(dmab, return 0);
-
- mutex_lock(&list_mutex);
- list_for_each(p, &mem_list_head) {
- mem = list_entry(p, struct snd_mem_list, 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(p);
- *dmab = mem->buffer;
- if (dmab->dev.dev == NULL)
- dmab->dev.dev = dev;
- kfree(mem);
- mutex_unlock(&list_mutex);
- return dmab->bytes;
- }
- }
- mutex_unlock(&list_mutex);
- return 0;
-}
-
-/**
- * snd_dma_reserve_buf - reserve the buffer
- * @dmab: the buffer to reserve
- * @id: the buffer id
- *
- * Reserves the given buffer as a reserved buffer.
- *
- * Returns zero if successful, or a negative code at error.
- */
-int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
-{
- struct snd_mem_list *mem;
-
- snd_assert(dmab, return -EINVAL);
- mem = kmalloc(sizeof(*mem), GFP_KERNEL);
- if (! mem)
- return -ENOMEM;
- mutex_lock(&list_mutex);
- mem->buffer = *dmab;
- mem->id = id;
- list_add_tail(&mem->list, &mem_list_head);
- mutex_unlock(&list_mutex);
- return 0;
-}
-
-/*
- * purge all reserved buffers
- */
-static void free_all_reserved_pages(void)
-{
- struct list_head *p;
- struct snd_mem_list *mem;
-
- mutex_lock(&list_mutex);
- while (! list_empty(&mem_list_head)) {
- p = mem_list_head.next;
- mem = list_entry(p, struct snd_mem_list, list);
- list_del(p);
- snd_dma_free_pages(&mem->buffer);
- kfree(mem);
- }
- mutex_unlock(&list_mutex);
-}
-
-
-#ifdef CONFIG_PROC_FS
-/*
- * proc file interface
- */
-#define SND_MEM_PROC_FILE "driver/snd-page-alloc"
-static struct proc_dir_entry *snd_mem_proc;
-
-static int snd_mem_proc_read(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int len = 0;
- long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
- struct list_head *p;
- struct snd_mem_list *mem;
- int devno;
- static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
-
- mutex_lock(&list_mutex);
- len += snprintf(page + len, count - len,
- "pages : %li bytes (%li pages per %likB)\n",
- pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
- devno = 0;
- list_for_each(p, &mem_list_head) {
- mem = list_entry(p, struct snd_mem_list, list);
- devno++;
- len += snprintf(page + len, count - len,
- "buffer %d : ID %08x : type %s\n",
- devno, mem->id, types[mem->buffer.dev.type]);
- len += snprintf(page + len, count - len,
- " addr = 0x%lx, size = %d bytes\n",
- (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes);
- }
- mutex_unlock(&list_mutex);
- return len;
-}
-
-/* FIXME: for pci only - other bus? */
-#ifdef CONFIG_PCI
-#define gettoken(bufp) strsep(bufp, " \t\n")
-
-static int snd_mem_proc_write(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
-{
- char buf[128];
- char *token, *p;
-
- if (count > ARRAY_SIZE(buf) - 1)
- count = ARRAY_SIZE(buf) - 1;
- if (copy_from_user(buf, buffer, count))
- return -EFAULT;
- buf[ARRAY_SIZE(buf) - 1] = '\0';
-
- p = buf;
- token = gettoken(&p);
- if (! token || *token == '#')
- return (int)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 (int)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);
- return (int)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 (int)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 (int)count;
-}
-#endif /* CONFIG_PCI */
-#endif /* CONFIG_PROC_FS */
-
-/*
- * module entry
- */
-
-static int __init snd_mem_init(void)
-{
-#ifdef CONFIG_PROC_FS
- snd_mem_proc = create_proc_entry(SND_MEM_PROC_FILE, 0644, NULL);
- if (snd_mem_proc) {
- snd_mem_proc->read_proc = snd_mem_proc_read;
-#ifdef CONFIG_PCI
- snd_mem_proc->write_proc = snd_mem_proc_write;
#endif
+ default:
+ pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type);
}
-#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
*/
@@ -643,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 93537ab7c2a..36c0f1a2e18 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* Misc memory accessors
*
@@ -20,9 +20,10 @@
*
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <asm/io.h>
#include <asm/uaccess.h>
+#include <sound/core.h>
/**
* copy_to_user_fromio - copy data from mmio-space to user-space
@@ -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 03fc711f412..30e027ecf4d 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -1,6 +1,6 @@
/*
* Misc and compatibility things
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,13 +19,28 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#include <linux/time.h>
+#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/core.h>
+#ifdef CONFIG_SND_DEBUG
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define DEFAULT_DEBUG_LEVEL 2
+#else
+#define DEFAULT_DEBUG_LEVEL 1
+#endif
+
+static int debug = DEFAULT_DEBUG_LEVEL;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
+
+#endif /* CONFIG_SND_DEBUG */
+
void release_and_free_resource(struct resource *res)
{
if (res) {
@@ -37,44 +52,102 @@ void release_and_free_resource(struct resource *res)
EXPORT_SYMBOL(release_and_free_resource);
#ifdef CONFIG_SND_VERBOSE_PRINTK
-void snd_verbose_printk(const char *file, int line, const char *format, ...)
+/* strip the leading path if the given path is absolute */
+static const char *sanity_file_name(const char *path)
{
- va_list args;
-
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
- char tmp[] = "<0>";
- tmp[1] = format[1];
- printk("%sALSA %s:%d: ", tmp, file, line);
- format += 3;
- } else {
- printk("ALSA %s:%d: ", file, line);
- }
- va_start(args, format);
- vprintk(format, args);
- va_end(args);
+ if (*path == '/')
+ return strrchr(path, '/') + 1;
+ else
+ return path;
}
-
-EXPORT_SYMBOL(snd_verbose_printk);
#endif
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-void snd_verbose_printd(const char *file, int line, const char *format, ...)
+#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
+void __snd_printk(unsigned int level, const char *path, int line,
+ const char *format, ...)
{
va_list args;
-
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
- char tmp[] = "<0>";
- tmp[1] = format[1];
- printk("%sALSA %s:%d: ", tmp, file, line);
- format += 3;
- } else {
- printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
- }
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+ int kern_level;
+ struct va_format vaf;
+ char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV";
+#endif
+
+#ifdef CONFIG_SND_DEBUG
+ if (debug < level)
+ return;
+#endif
+
va_start(args, format);
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ kern_level = printk_get_level(format);
+ if (kern_level) {
+ const char *end_of_header = printk_skip_level(format);
+ memcpy(verbose_fmt, format, end_of_header - format);
+ vaf.fmt = end_of_header;
+ } else if (level)
+ memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1);
+ printk(verbose_fmt, sanity_file_name(path), line, &vaf);
+
+#else
vprintk(format, args);
+#endif
va_end(args);
+}
+EXPORT_SYMBOL_GPL(__snd_printk);
+#endif
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+/**
+ * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list
+ * @vendor: PCI SSV id
+ * @device: PCI SSD id
+ * @list: quirk list, terminated by a null entry
+ *
+ * Look through the given quirk list and finds a matching entry
+ * with the same PCI SSID. When subdevice is 0, all subdevice
+ * values may match.
+ *
+ * Returns the matched entry pointer, or NULL if nothing matched.
+ */
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup_id(u16 vendor, u16 device,
+ const struct snd_pci_quirk *list)
+{
+ const struct snd_pci_quirk *q;
+
+ for (q = list; q->subvendor; q++) {
+ if (q->subvendor != vendor)
+ continue;
+ if (!q->subdevice ||
+ (device & q->subdevice_mask) == q->subdevice)
+ return q;
+ }
+ return NULL;
}
+EXPORT_SYMBOL(snd_pci_quirk_lookup_id);
-EXPORT_SYMBOL(snd_verbose_printd);
+/**
+ * snd_pci_quirk_lookup - look up a PCI SSID quirk list
+ * @pci: pci_dev handle
+ * @list: quirk list, terminated by a null entry
+ *
+ * Look through the given quirk list and finds a matching entry
+ * with the same PCI SSID. When subdevice is 0, all subdevice
+ * values may match.
+ *
+ * Returns the matched entry pointer, or NULL if nothing matched.
+ */
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
+{
+ return snd_pci_quirk_lookup_id(pci->subsystem_vendor,
+ pci->subsystem_device,
+ list);
+}
+EXPORT_SYMBOL(snd_pci_quirk_lookup);
#endif
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
index e6d5a045ba2..10a79453245 100644
--- a/sound/core/oss/Makefile
+++ b/sound/core/oss/Makefile
@@ -1,12 +1,13 @@
#
# Makefile for ALSA
-# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
snd-mixer-oss-objs := mixer_oss.o
-snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
- io.o copy.o linear.o mulaw.o route.o rate.o
+snd-pcm-oss-y := pcm_oss.o
+snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \
+ io.o copy.o linear.o mulaw.o route.o rate.o
obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
index 6658facc5cd..05b58d4fc2b 100644
--- a/sound/core/oss/copy.c
+++ b/sound/core/oss/copy.c
@@ -19,10 +19,6 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -36,17 +32,18 @@ static snd_pcm_sframes_t copy_transfer(struct snd_pcm_plugin *plugin,
unsigned int channel;
unsigned int nchannels;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
nchannels = plugin->src_format.channels;
for (channel = 0; channel < nchannels; channel++) {
- snd_assert(src_channels->area.first % 8 == 0 &&
- src_channels->area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels->area.first % 8 == 0 &&
- dst_channels->area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels->area.first % 8 ||
+ src_channels->area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels->area.first % 8 ||
+ dst_channels->area.step % 8))
+ return -ENXIO;
if (!src_channels->enabled) {
if (dst_channels->wanted)
snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
@@ -70,15 +67,20 @@ int snd_pcm_plugin_build_copy(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
int width;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->format == dst_format->format, return -ENXIO);
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ if (snd_BUG_ON(src_format->format != dst_format->format))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
width = snd_pcm_format_physical_width(src_format->format);
- snd_assert(width > 0, return -ENXIO);
+ if (snd_BUG_ON(width <= 0))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
0, &plugin);
@@ -88,5 +90,3 @@ int snd_pcm_plugin_build_copy(struct snd_pcm_substream *plug,
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index b6e7ce30e5a..6faa1d71920 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -1,6 +1,6 @@
/*
* PCM I/O Plug-In Interface
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
*
* This library is free software; you can redistribute it and/or modify
@@ -19,10 +19,6 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,14 +39,17 @@ static snd_pcm_sframes_t io_playback_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- snd_assert(plugin != NULL, return -ENXIO);
- snd_assert(src_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
+ if (snd_BUG_ON(!src_channels))
+ return -ENXIO;
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
return pcm_write(plugin->plug, src_channels->area.addr, frames);
} else {
int channel, channels = plugin->dst_format.channels;
void **bufs = (void**)plugin->extra_data;
- snd_assert(bufs != NULL, return -ENXIO);
+ if (snd_BUG_ON(!bufs))
+ return -ENXIO;
for (channel = 0; channel < channels; channel++) {
if (src_channels[channel].enabled)
bufs[channel] = src_channels[channel].area.addr;
@@ -66,14 +65,17 @@ static snd_pcm_sframes_t io_capture_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- snd_assert(plugin != NULL, return -ENXIO);
- snd_assert(dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
+ if (snd_BUG_ON(!dst_channels))
+ return -ENXIO;
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
return pcm_read(plugin->plug, dst_channels->area.addr, frames);
} else {
int channel, channels = plugin->dst_format.channels;
void **bufs = (void**)plugin->extra_data;
- snd_assert(bufs != NULL, return -ENXIO);
+ if (snd_BUG_ON(!bufs))
+ return -ENXIO;
for (channel = 0; channel < channels; channel++) {
if (dst_channels[channel].enabled)
bufs[channel] = dst_channels[channel].area.addr;
@@ -111,9 +113,11 @@ int snd_pcm_plugin_build_io(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format format;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(plug != NULL && params != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug || !params))
+ return -ENXIO;
format.format = params_format(params);
format.rate = params_rate(params);
format.channels = params_channels(params);
@@ -135,5 +139,3 @@ int snd_pcm_plugin_build_io(struct snd_pcm_substream *plug,
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 5b1bcdc6477..2045697f449 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -1,6 +1,6 @@
/*
* Linear conversion Plug-In
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>,
* Abramo Bagnara <abramo@alsa-project.org>
*
*
@@ -20,10 +20,6 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -34,19 +30,34 @@
*/
struct linear_priv {
- int conv;
+ int cvt_endian; /* need endian conversion? */
+ unsigned int src_ofs; /* byte offset in source format */
+ unsigned int dst_ofs; /* byte soffset in destination format */
+ unsigned int copy_ofs; /* byte offset in temporary u32 data */
+ unsigned int dst_bytes; /* byte size of destination format */
+ unsigned int copy_bytes; /* bytes to copy per conversion */
+ unsigned int flip; /* MSB flip for signeness, done after endian conv */
};
+static inline void do_convert(struct linear_priv *data,
+ unsigned char *dst, unsigned char *src)
+{
+ unsigned int tmp = 0;
+ unsigned char *p = (unsigned char *)&tmp;
+
+ memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);
+ if (data->cvt_endian)
+ tmp = swab32(tmp);
+ tmp ^= data->flip;
+ memcpy(dst, p + data->dst_ofs, data->dst_bytes);
+}
+
static void convert(struct snd_pcm_plugin *plugin,
const struct snd_pcm_plugin_channel *src_channels,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
-#define CONV_LABELS
-#include "plugin_ops.h"
-#undef CONV_LABELS
struct linear_priv *data = (struct linear_priv *)plugin->extra_data;
- void *conv = conv_labels[data->conv];
int channel;
int nchannels = plugin->src_format.channels;
for (channel = 0; channel < nchannels; ++channel) {
@@ -67,11 +78,7 @@ static void convert(struct snd_pcm_plugin *plugin,
dst_step = dst_channels[channel].area.step / 8;
frames1 = frames;
while (frames1-- > 0) {
- goto *conv;
-#define CONV_END after
-#include "plugin_ops.h"
-#undef CONV_END
- after:
+ do_convert(data, dst, src);
src += src_step;
dst += dst_step;
}
@@ -83,22 +90,20 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- struct linear_priv *data;
-
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
- data = (struct linear_priv *)plugin->extra_data;
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -106,29 +111,37 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
return frames;
}
-static int conv_index(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_endian, dst_endian, sign, src_width, dst_width;
-
- sign = (snd_pcm_format_signed(src_format) !=
- snd_pcm_format_signed(dst_format));
-#ifdef SNDRV_LITTLE_ENDIAN
- src_endian = snd_pcm_format_big_endian(src_format);
- dst_endian = snd_pcm_format_big_endian(dst_format);
-#else
- src_endian = snd_pcm_format_little_endian(src_format);
- dst_endian = snd_pcm_format_little_endian(dst_format);
-#endif
-
- if (src_endian < 0)
- src_endian = 0;
- if (dst_endian < 0)
- dst_endian = 0;
-
- src_width = snd_pcm_format_width(src_format) / 8 - 1;
- dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
-
- return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
+ int src_le, dst_le, src_bytes, dst_bytes;
+
+ src_bytes = snd_pcm_format_width(src_format) / 8;
+ dst_bytes = snd_pcm_format_width(dst_format) / 8;
+ src_le = snd_pcm_format_little_endian(src_format) > 0;
+ dst_le = snd_pcm_format_little_endian(dst_format) > 0;
+
+ data->dst_bytes = dst_bytes;
+ data->cvt_endian = src_le != dst_le;
+ data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;
+ if (src_le) {
+ data->copy_ofs = 4 - data->copy_bytes;
+ data->src_ofs = src_bytes - data->copy_bytes;
+ } else
+ data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -
+ src_bytes;
+ if (dst_le)
+ data->dst_ofs = 4 - data->dst_bytes;
+ else
+ data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -
+ dst_bytes;
+ if (snd_pcm_format_signed(src_format) !=
+ snd_pcm_format_signed(dst_format)) {
+ if (dst_le)
+ data->flip = (__force u32)cpu_to_le32(0x80000000);
+ else
+ data->flip = (__force u32)cpu_to_be32(0x80000000);
+ }
}
int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
@@ -140,13 +153,17 @@ int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
struct linear_priv *data;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
- snd_assert(snd_pcm_format_linear(src_format->format) &&
- snd_pcm_format_linear(dst_format->format), return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
+ if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
+ !snd_pcm_format_linear(dst_format->format)))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "linear format conversion",
src_format, dst_format,
@@ -154,10 +171,8 @@ int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
if (err < 0)
return err;
data = (struct linear_priv *)plugin->extra_data;
- data->conv = conv_index(src_format->format, dst_format->format);
+ init_data(data, src_format->format, dst_format->format);
plugin->transfer = linear_transfer;
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index f4c67042e3a..5e6349f00ec 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1,6 +1,6 @@
/*
* OSS emulation layer for the mixer interface
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,12 +19,11 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#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>
@@ -34,7 +33,7 @@
#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
MODULE_LICENSE("GPL");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);
@@ -45,18 +44,27 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
struct snd_mixer_oss_file *fmixer;
int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
card = snd_lookup_oss_minor_data(iminor(inode),
SNDRV_OSS_DEVICE_TYPE_MIXER);
if (card == NULL)
return -ENODEV;
- if (card->mixer_oss == NULL)
+ if (card->mixer_oss == NULL) {
+ snd_card_unref(card);
return -ENODEV;
+ }
err = snd_card_file_add(card, file);
- if (err < 0)
+ if (err < 0) {
+ snd_card_unref(card);
return err;
+ }
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
if (fmixer == NULL) {
snd_card_file_remove(card, file);
+ snd_card_unref(card);
return -ENOMEM;
}
fmixer->card = card;
@@ -65,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;
}
@@ -75,7 +85,7 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file)
struct snd_mixer_oss_file *fmixer;
if (file->private_data) {
- fmixer = (struct snd_mixer_oss_file *) file->private_data;
+ fmixer = file->private_data;
module_put(fmixer->card->module);
snd_card_file_remove(fmixer->card, file);
kfree(fmixer);
@@ -188,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;
@@ -212,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)
@@ -220,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];
@@ -259,8 +271,10 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
result = pslot->get_volume(fmixer, pslot, &left, &right);
if (!pslot->stereo)
right = left;
- snd_assert(left >= 0 && left <= 100, return -EIO);
- snd_assert(right >= 0 && right <= 100, return -EIO);
+ if (snd_BUG_ON(left < 0 || left > 100))
+ return -EIO;
+ if (snd_BUG_ON(right < 0 || right > 100))
+ return -EIO;
if (result >= 0) {
pslot->volume[0] = left;
pslot->volume[1] = right;
@@ -300,7 +314,8 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int
int __user *p = argp;
int tmp;
- snd_assert(fmixer != NULL, return -ENXIO);
+ if (snd_BUG_ON(!fmixer))
+ return -ENXIO;
if (((cmd >> 8) & 0xff) == 'M') {
switch (cmd) {
case SOUND_MIXER_INFO:
@@ -363,14 +378,15 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int
static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg);
+ return snd_mixer_oss_ioctl1(file->private_data, cmd, arg);
}
int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)
{
struct snd_mixer_oss_file fmixer;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
if (card->mixer_oss == NULL)
return -ENXIO;
memset(&fmixer, 0, sizeof(fmixer));
@@ -390,11 +406,12 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l
* REGISTRATION PART
*/
-static struct file_operations snd_mixer_oss_f_ops =
+static const struct file_operations snd_mixer_oss_f_ops =
{
.owner = THIS_MODULE,
.open = snd_mixer_oss_open,
.release = snd_mixer_oss_release,
+ .llseek = no_llseek,
.unlocked_ioctl = snd_mixer_oss_ioctl,
.compat_ioctl = snd_mixer_oss_ioctl_compat,
};
@@ -490,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);
}
@@ -575,7 +592,7 @@ static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *left, int *right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
*left = *right = 100;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
@@ -611,8 +628,10 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
return;
+ }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
@@ -651,7 +670,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
return;
down_read(&card->controls_rwsem);
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&fmixer->card->controls_rwsem);
+ up_read(&card->controls_rwsem);
return;
}
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
@@ -684,12 +703,15 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int left, int right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot,
+ slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
@@ -698,19 +720,27 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
if (left || right) {
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
} else {
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
}
@@ -722,7 +752,7 @@ static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -735,7 +765,7 @@ static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -748,7 +778,7 @@ static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
return 0;
@@ -758,7 +788,7 @@ static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
return 0;
@@ -779,7 +809,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL) {
err = -ENOMEM;
- goto __unlock;
+ goto __free_only;
}
down_read(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
@@ -795,7 +825,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -808,6 +838,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = 0;
__unlock:
up_read(&card->controls_rwsem);
+ __free_only:
kfree(uctl);
kfree(uinfo);
return err;
@@ -829,7 +860,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL) {
err = -ENOMEM;
- goto __unlock;
+ goto __free_only;
}
down_read(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
@@ -843,7 +874,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -862,6 +893,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = 0;
__unlock:
up_read(&card->controls_rwsem);
+ __free_only:
kfree(uctl);
kfree(uinfo);
return err;
@@ -907,7 +939,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl
static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
{
- struct slot *p = (struct slot *)chn->private_data;
+ struct slot *p = chn->private_data;
if (p) {
if (p->allocated && p->assigned) {
kfree(p->assigned->name);
@@ -926,6 +958,68 @@ static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)
rslot->number = idx;
}
+/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
+ snd_mixer_oss_build_input! */
+static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
+ struct snd_mixer_oss_assign_table *ptr,
+ struct slot *slot)
+{
+ char str[64];
+ int err;
+
+ err = snd_mixer_oss_build_test(mixer, slot, ptr->name, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GLOBAL);
+ if (err)
+ return err;
+ sprintf(str, "%s Switch", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GSWITCH);
+ if (err)
+ return err;
+ sprintf(str, "%s Route", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GROUTE);
+ if (err)
+ return err;
+ sprintf(str, "%s Volume", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GVOLUME);
+ if (err)
+ return err;
+ sprintf(str, "%s Playback Switch", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PSWITCH);
+ if (err)
+ return err;
+ sprintf(str, "%s Playback Route", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PROUTE);
+ if (err)
+ return err;
+ sprintf(str, "%s Playback Volume", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PVOLUME);
+ if (err)
+ return err;
+ sprintf(str, "%s Capture Switch", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CSWITCH);
+ if (err)
+ return err;
+ sprintf(str, "%s Capture Route", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CROUTE);
+ if (err)
+ return err;
+ sprintf(str, "%s Capture Volume", ptr->name);
+ err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CVOLUME);
+ if (err)
+ return err;
+
+ return 0;
+}
+
/*
* build an OSS mixer element.
* ptr_allocated means the entry is dynamically allocated (change via proc file).
@@ -945,44 +1039,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
memset(&slot, 0, sizeof(slot));
memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
- if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
- SNDRV_MIXER_OSS_ITEM_GLOBAL))
- return 0;
- sprintf(str, "%s Switch", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_GSWITCH))
- return 0;
- sprintf(str, "%s Route", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_GROUTE))
- return 0;
- sprintf(str, "%s Volume", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_GVOLUME))
- return 0;
- sprintf(str, "%s Playback Switch", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_PSWITCH))
- return 0;
- sprintf(str, "%s Playback Route", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_PROUTE))
- return 0;
- sprintf(str, "%s Playback Volume", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_PVOLUME))
- return 0;
- sprintf(str, "%s Capture Switch", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_CSWITCH))
- return 0;
- sprintf(str, "%s Capture Route", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_CROUTE))
- return 0;
- sprintf(str, "%s Capture Volume", ptr->name);
- if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
- SNDRV_MIXER_OSS_ITEM_CVOLUME))
+ if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
return 0;
down_read(&mixer->card->controls_rwsem);
if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
@@ -996,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);
@@ -1011,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)) {
@@ -1023,7 +1082,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
}
up_read(&mixer->card->controls_rwsem);
if (slot.present != 0) {
- pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
+ pslot = kmalloc(sizeof(slot), GFP_KERNEL);
if (! pslot)
return -ENOMEM;
*pslot = slot;
@@ -1116,7 +1175,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_mixer_oss *mixer = entry->private_data;
- char line[128], str[32], idxstr[16], *cptr;
+ char line[128], str[32], idxstr[16];
+ const char *cptr;
int ch, idx;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
@@ -1127,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));
@@ -1141,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);
@@ -1152,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;
@@ -1212,7 +1273,9 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{ SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
{ SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
{ SOUND_MIXER_PCM, "PCM", 0 },
- { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
+ { SOUND_MIXER_SPEAKER, "Beep", 0 },
+ { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, /* fallback */
+ { SOUND_MIXER_SPEAKER, "Speaker", 0 }, /* fallback */
{ SOUND_MIXER_LINE, "Line", 0 },
{ SOUND_MIXER_MIC, "Mic", 0 },
{ SOUND_MIXER_CD, "CD", 0 },
@@ -1234,6 +1297,8 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{ SOUND_MIXER_DIGITAL3, "Digital", 2 },
{ SOUND_MIXER_PHONEIN, "Phone", 0 },
{ SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
+ { SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/
+ { SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/
{ SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
{ SOUND_MIXER_VIDEO, "Video", 0 },
{ SOUND_MIXER_RADIO, "Radio", 0 },
@@ -1259,9 +1324,11 @@ static int snd_mixer_oss_free1(void *private)
struct snd_card *card;
int idx;
- snd_assert(mixer != NULL, return -ENXIO);
+ if (!mixer)
+ return 0;
card = mixer->card;
- snd_assert(mixer == card->mixer_oss, return -ENXIO);
+ if (snd_BUG_ON(mixer != card->mixer_oss))
+ return -ENXIO;
card->mixer_oss = NULL;
for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
struct snd_mixer_oss_slot *chn = &mixer->slots[idx];
@@ -1277,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;
}
@@ -1299,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 2eb18807e6d..7915564bd39 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -1,6 +1,6 @@
/*
* Mu-Law conversion Plug-In Interface
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
* Uros Bizjak <uros@kss-loka.si>
*
* Based on reference implementation by Sun Microsystems, Inc.
@@ -21,10 +21,6 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -149,19 +145,32 @@ typedef void (*mulaw_f)(struct snd_pcm_plugin *plugin,
struct mulaw_priv {
mulaw_f func;
- int conv;
+ int cvt_endian; /* need endian conversion? */
+ unsigned int native_ofs; /* byte offset in native format */
+ unsigned int copy_ofs; /* byte offset in s16 format */
+ unsigned int native_bytes; /* byte size of the native format */
+ unsigned int copy_bytes; /* bytes to copy per conversion */
+ u16 flip; /* MSB flip for signedness, done after endian conversion */
};
+static inline void cvt_s16_to_native(struct mulaw_priv *data,
+ unsigned char *dst, u16 sample)
+{
+ sample ^= data->flip;
+ if (data->cvt_endian)
+ sample = swab16(sample);
+ if (data->native_bytes > data->copy_bytes)
+ memset(dst, 0, data->native_bytes);
+ memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs,
+ data->copy_bytes);
+}
+
static void mulaw_decode(struct snd_pcm_plugin *plugin,
const struct snd_pcm_plugin_channel *src_channels,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
-#define PUT_S16_LABELS
-#include "plugin_ops.h"
-#undef PUT_S16_LABELS
struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
- void *put = put_s16_labels[data->conv];
int channel;
int nchannels = plugin->src_format.channels;
for (channel = 0; channel < nchannels; ++channel) {
@@ -183,30 +192,33 @@ static void mulaw_decode(struct snd_pcm_plugin *plugin,
frames1 = frames;
while (frames1-- > 0) {
signed short sample = ulaw2linear(*src);
- goto *put;
-#define PUT_S16_END after
-#include "plugin_ops.h"
-#undef PUT_S16_END
- after:
+ cvt_s16_to_native(data, dst, sample);
src += src_step;
dst += dst_step;
}
}
}
+static inline signed short cvt_native_to_s16(struct mulaw_priv *data,
+ unsigned char *src)
+{
+ u16 sample = 0;
+ memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs,
+ data->copy_bytes);
+ if (data->cvt_endian)
+ sample = swab16(sample);
+ sample ^= data->flip;
+ return (signed short)sample;
+}
+
static void mulaw_encode(struct snd_pcm_plugin *plugin,
const struct snd_pcm_plugin_channel *src_channels,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
-#define GET_S16_LABELS
-#include "plugin_ops.h"
-#undef GET_S16_LABELS
struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
- void *get = get_s16_labels[data->conv];
int channel;
int nchannels = plugin->src_format.channels;
- signed short sample = 0;
for (channel = 0; channel < nchannels; ++channel) {
char *src;
char *dst;
@@ -225,11 +237,7 @@ static void mulaw_encode(struct snd_pcm_plugin *plugin,
dst_step = dst_channels[channel].area.step / 8;
frames1 = frames;
while (frames1-- > 0) {
- goto *get;
-#define GET_S16_END after
-#include "plugin_ops.h"
-#undef GET_S16_END
- after:
+ signed short sample = cvt_native_to_s16(data, src);
*dst = linear2ulaw(sample);
src += src_step;
dst += dst_step;
@@ -244,19 +252,20 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
{
struct mulaw_priv *data;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -265,23 +274,25 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
return frames;
}
-static int getput_index(int format)
+static void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
{
- int sign, width, endian;
- sign = !snd_pcm_format_signed(format);
- width = snd_pcm_format_width(format) / 8 - 1;
- if (width < 0 || width > 3) {
- snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
- width = 0;
- }
#ifdef SNDRV_LITTLE_ENDIAN
- endian = snd_pcm_format_big_endian(format);
+ data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
#else
- endian = snd_pcm_format_little_endian(format);
+ data->cvt_endian = snd_pcm_format_little_endian(format) > 0;
#endif
- if (endian < 0)
- endian = 0;
- return width * 4 + endian * 2 + sign;
+ if (!snd_pcm_format_signed(format))
+ data->flip = 0x8000;
+ data->native_bytes = snd_pcm_format_physical_width(format) / 8;
+ data->copy_bytes = data->native_bytes < 2 ? 1 : 2;
+ if (snd_pcm_format_little_endian(format)) {
+ data->native_ofs = data->native_bytes - data->copy_bytes;
+ data->copy_ofs = 2 - data->copy_bytes;
+ } else {
+ /* S24 in 4bytes need an 1 byte offset */
+ data->native_ofs = data->native_bytes -
+ snd_pcm_format_width(format) / 8;
+ }
}
int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
@@ -295,11 +306,14 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format *format;
mulaw_f func;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
format = src_format;
@@ -313,7 +327,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
snd_BUG();
return -EINVAL;
}
- snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+ if (snd_BUG_ON(!snd_pcm_format_linear(format->format)))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
src_format, dst_format,
@@ -322,11 +337,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
return err;
data = (struct mulaw_priv *)plugin->extra_data;
data->func = func;
- data->conv = getput_index(format->format);
- snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
+ init_data(data, format->format);
plugin->transfer = mulaw_transfer;
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index e0821eb3d85..ada69d7a8d7 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer / OSS compatible
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -26,13 +26,12 @@
#define OSS_DEBUG
#endif
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/math64.h>
#include <linux/string.h>
#include <sound/core.h>
#include <sound/minors.h>
@@ -42,14 +41,15 @@
#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@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
MODULE_LICENSE("GPL");
module_param_array(dsp_map, int, NULL, 0444);
@@ -61,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);
@@ -454,7 +453,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
- snd_assert(max >= 0, return -EINVAL);
+ if (max < 0) {
+ kfree(save);
+ return max;
+ }
last = 1;
}
_end:
@@ -463,7 +465,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
- snd_assert(v >= 0, return -EINVAL);
+ snd_BUG_ON(v < 0);
return v;
}
@@ -618,9 +620,7 @@ static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames)
#else
{
u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
- u32 rem;
- div64_32(&bsize, buffer_size, &rem);
- return (long)bsize;
+ return div_u64(bsize, buffer_size);
}
#endif
}
@@ -634,7 +634,29 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)
return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
}
-static int snd_pcm_oss_format_from(int format)
+static inline
+snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
+{
+ return runtime->hw_ptr_interrupt;
+}
+
+/* define extended formats in the recent OSS versions (if any) */
+/* linear formats */
+#define AFMT_S32_LE 0x00001000
+#define AFMT_S32_BE 0x00002000
+#define AFMT_S24_LE 0x00008000
+#define AFMT_S24_BE 0x00010000
+#define AFMT_S24_PACKED 0x00040000
+
+/* other supported formats */
+#define AFMT_FLOAT 0x00004000
+#define AFMT_SPDIF_RAW 0x00020000
+
+/* unsupported formats */
+#define AFMT_AC3 0x00000400
+#define AFMT_VORBIS 0x00000800
+
+static snd_pcm_format_t snd_pcm_oss_format_from(int format)
{
switch (format) {
case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
@@ -647,11 +669,18 @@ static int snd_pcm_oss_format_from(int format)
case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;
case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;
case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG;
+ case AFMT_S32_LE: return SNDRV_PCM_FORMAT_S32_LE;
+ case AFMT_S32_BE: return SNDRV_PCM_FORMAT_S32_BE;
+ case AFMT_S24_LE: return SNDRV_PCM_FORMAT_S24_LE;
+ case AFMT_S24_BE: return SNDRV_PCM_FORMAT_S24_BE;
+ case AFMT_S24_PACKED: return SNDRV_PCM_FORMAT_S24_3LE;
+ case AFMT_FLOAT: return SNDRV_PCM_FORMAT_FLOAT;
+ case AFMT_SPDIF_RAW: return SNDRV_PCM_FORMAT_IEC958_SUBFRAME;
default: return SNDRV_PCM_FORMAT_U8;
}
}
-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;
@@ -664,6 +693,13 @@ static int snd_pcm_oss_format_to(int format)
case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG;
+ case SNDRV_PCM_FORMAT_S32_LE: return AFMT_S32_LE;
+ case SNDRV_PCM_FORMAT_S32_BE: return AFMT_S32_BE;
+ case SNDRV_PCM_FORMAT_S24_LE: return AFMT_S24_LE;
+ case SNDRV_PCM_FORMAT_S24_BE: return AFMT_S24_BE;
+ case SNDRV_PCM_FORMAT_S24_3LE: return AFMT_S24_PACKED;
+ case SNDRV_PCM_FORMAT_FLOAT: return AFMT_FLOAT;
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW;
default: return -EINVAL;
}
}
@@ -750,7 +786,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
while (oss_period_size * oss_periods > oss_buffer_size)
oss_period_size /= 2;
- snd_assert(oss_period_size >= 16, return -EINVAL);
+ if (oss_period_size < 16)
+ return -EINVAL;
runtime->oss.period_bytes = oss_period_size;
runtime->oss.period_frames = 1;
runtime->oss.periods = oss_periods;
@@ -806,15 +843,18 @@ 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;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -EINTR;
sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
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;
}
@@ -829,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;
}
@@ -852,29 +892,33 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
else
sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
- if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
- for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
- if (snd_mask_test(&sformat_mask, sformat) &&
+ if ((__force int)sformat < 0 ||
+ !snd_mask_test(&sformat_mask, (__force int)sformat)) {
+ for (sformat = (__force snd_pcm_format_t)0;
+ (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST;
+ sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {
+ if (snd_mask_test(&sformat_mask, (__force int)sformat) &&
snd_pcm_oss_format_to(sformat) >= 0)
break;
}
- if (sformat > SNDRV_PCM_FORMAT_LAST) {
- snd_printd("Cannot find a format!!!\n");
+ if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) {
+ pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
err = -EINVAL;
goto failure;
}
}
- err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
- snd_assert(err >= 0, goto failure);
+ err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
+ if (err < 0)
+ goto failure;
if (direct) {
memcpy(params, sparams, sizeof(*params));
} else {
_snd_pcm_hw_params_any(params);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
- SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+ (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
- snd_pcm_oss_format_from(runtime->oss.format), 0);
+ (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
runtime->oss.channels, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
@@ -898,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;
}
@@ -928,16 +974,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
- snd_assert(err >= 0, goto failure);
+ if (err < 0)
+ goto failure;
err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
runtime->oss.periods, NULL);
- snd_assert(err >= 0, goto failure);
+ if (err < 0)
+ goto failure;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
- snd_printd("HW_PARAMS failed: %i\n", err);
+ pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);
goto failure;
}
@@ -954,10 +1002,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
sw_params->stop_threshold = runtime->buffer_size;
sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
sw_params->period_step = 1;
- sw_params->sleep_min = 0;
sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
1 : runtime->period_size;
- sw_params->xfer_align = 1;
if (atomic_read(&substream->mmap_count) ||
substream->oss.setup.nosilence) {
sw_params->silence_threshold = 0;
@@ -972,13 +1018,16 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
}
if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
- snd_printd("SW_PARAMS failed: %i\n", err);
+ pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);
goto failure;
}
runtime->oss.periods = params_periods(sparams);
oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
- snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
+ if (oss_period_size < 0) {
+ err = -EINVAL;
+ goto failure;
+ }
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
if (runtime->oss.plugin_first) {
err = snd_pcm_plug_alloc(substream, oss_period_size);
@@ -989,7 +1038,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
oss_period_size *= oss_frame_size;
oss_buffer_size = oss_period_size * runtime->oss.periods;
- snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
+ if (oss_buffer_size < 0) {
+ err = -EINVAL;
+ goto failure;
+ }
runtime->oss.period_bytes = oss_period_size;
runtime->oss.buffer_bytes = oss_buffer_size;
@@ -1005,10 +1057,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.channels = params_channels(params);
runtime->oss.rate = params_rate(params);
- runtime->oss.params = 0;
- runtime->oss.prepare = 1;
vfree(runtime->oss.buffer);
runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ if (!runtime->oss.buffer) {
+ err = -ENOMEM;
+ goto failure;
+ }
+
+ runtime->oss.params = 0;
+ runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
if (runtime->dma_area)
snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
@@ -1020,6 +1077,7 @@ failure:
kfree(sw_params);
kfree(params);
kfree(sparams);
+ mutex_unlock(&runtime->oss.params_lock);
return err;
}
@@ -1040,7 +1098,8 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
return err;
}
}
- snd_assert(asubstream != NULL, return -EIO);
+ if (!asubstream)
+ return -EIO;
if (r_substream)
*r_substream = asubstream;
return 0;
@@ -1053,11 +1112,12 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
if (err < 0) {
- snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+ pcm_dbg(substream->pcm,
+ "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
return err;
}
runtime->oss.prepare = 0;
- runtime->oss.prev_hw_ptr_interrupt = 0;
+ runtime->oss.prev_hw_ptr_period = 0;
runtime->oss.period_ptr = 0;
runtime->oss.buffer_used = 0;
@@ -1118,10 +1178,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: write: recovering from XRUN\n");
- else
- printk("pcm_oss: write: recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: write: recovering from %s\n",
+ runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
@@ -1130,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;
@@ -1154,10 +1214,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: read: recovering from XRUN\n");
- else
- printk("pcm_oss: read: recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read: recovering from %s\n",
+ runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
@@ -1173,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) {
@@ -1200,10 +1260,10 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: writev: recovering from XRUN\n");
- else
- printk("pcm_oss: writev: recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: writev: recovering from %s\n",
+ runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
@@ -1236,10 +1296,10 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void *
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: readv: recovering from XRUN\n");
- else
- printk("pcm_oss: readv: recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: readv: recovering from %s\n",
+ runtime->status->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
@@ -1272,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;
}
@@ -1307,14 +1367,17 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
+ mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
if (tmp > 0) {
- if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp))
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) {
+ tmp = -EFAULT;
+ goto err;
+ }
}
runtime->oss.buffer_used += tmp;
buf += tmp;
@@ -1325,22 +1388,24 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr,
runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
if (tmp <= 0)
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ goto err;
runtime->oss.bytes += tmp;
runtime->oss.period_ptr += tmp;
runtime->oss.period_ptr %= runtime->oss.period_bytes;
if (runtime->oss.period_ptr == 0 ||
runtime->oss.period_ptr == runtime->oss.buffer_used)
runtime->oss.buffer_used = 0;
- else if ((substream->f_flags & O_NONBLOCK) != 0)
- return xfer > 0 ? xfer : -EAGAIN;
+ else if ((substream->f_flags & O_NONBLOCK) != 0) {
+ tmp = -EAGAIN;
+ goto err;
+ }
}
} else {
tmp = snd_pcm_oss_write2(substream,
(const char __force *)buf,
runtime->oss.period_bytes, 0);
if (tmp <= 0)
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ goto err;
runtime->oss.bytes += tmp;
buf += tmp;
bytes -= tmp;
@@ -1350,7 +1415,12 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
break;
}
}
+ mutex_unlock(&runtime->oss.params_lock);
return xfer;
+
+ err:
+ mutex_unlock(&runtime->oss.params_lock);
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel)
@@ -1358,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;
@@ -1397,12 +1467,13 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
+ mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
if (tmp <= 0)
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ goto err;
runtime->oss.bytes += tmp;
runtime->oss.period_ptr = tmp;
runtime->oss.buffer_used = tmp;
@@ -1410,8 +1481,10 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
tmp = bytes;
if ((size_t) tmp > runtime->oss.buffer_used)
tmp = runtime->oss.buffer_used;
- if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp))
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) {
+ tmp = -EFAULT;
+ goto err;
+ }
buf += tmp;
bytes -= tmp;
xfer += tmp;
@@ -1420,29 +1493,37 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
tmp = snd_pcm_oss_read2(substream, (char __force *)buf,
runtime->oss.period_bytes, 0);
if (tmp <= 0)
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ goto err;
runtime->oss.bytes += tmp;
buf += tmp;
bytes -= tmp;
xfer += tmp;
}
}
+ mutex_unlock(&runtime->oss.params_lock);
return xfer;
+
+ err:
+ mutex_unlock(&runtime->oss.params_lock);
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
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;
}
@@ -1467,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;
@@ -1474,7 +1556,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
#ifdef OSS_DEBUG
- printk("sync1: size = %li\n", size);
+ pcm_dbg(substream->pcm, "sync1: size = %li\n", size);
#endif
while (1) {
result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
@@ -1488,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;
}
@@ -1500,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;
}
@@ -1528,28 +1611,33 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
return err;
format = snd_pcm_oss_format_from(runtime->oss.format);
width = snd_pcm_format_physical_width(format);
+ mutex_lock(&runtime->oss.params_lock);
if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
- printk("sync: buffer_used\n");
+ pcm_dbg(substream->pcm, "sync: buffer_used\n");
#endif
size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
snd_pcm_format_set_silence(format,
runtime->oss.buffer + runtime->oss.buffer_used,
size);
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
- if (err < 0)
+ if (err < 0) {
+ mutex_unlock(&runtime->oss.params_lock);
return err;
+ }
} else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
- printk("sync: period_ptr\n");
+ pcm_dbg(substream->pcm, "sync: period_ptr\n");
#endif
size = runtime->oss.period_bytes - runtime->oss.period_ptr;
snd_pcm_format_set_silence(format,
runtime->oss.buffer,
size * 8 / width);
err = snd_pcm_oss_sync1(substream, size);
- if (err < 0)
+ if (err < 0) {
+ mutex_unlock(&runtime->oss.params_lock);
return err;
+ }
}
/*
* The ALSA's period might be a bit large than OSS one.
@@ -1569,8 +1657,9 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
snd_pcm_format_set_silence(runtime->format,
runtime->oss.buffer,
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) {
@@ -1579,6 +1668,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
snd_pcm_lib_writev(substream, buffers, size);
}
}
+ mutex_unlock(&runtime->oss.params_lock);
/*
* finish sync: drain the buffer
*/
@@ -1699,7 +1789,10 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
return AFMT_MU_LAW | AFMT_U8 |
AFMT_S16_LE | AFMT_S16_BE |
AFMT_S8 | AFMT_U16_LE |
- AFMT_U16_BE;
+ AFMT_U16_BE |
+ AFMT_S32_LE | AFMT_S32_BE |
+ AFMT_S24_LE | AFMT_S24_BE |
+ AFMT_S24_PACKED;
params = kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
@@ -1707,7 +1800,8 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
err = snd_pcm_hw_refine(substream, params);
format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
kfree(params);
- snd_assert(err >= 0, return err);
+ if (err < 0)
+ return err;
for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
int f = snd_pcm_oss_format_to(fmt);
@@ -1825,7 +1919,9 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
static int snd_pcm_oss_nonblock(struct file * file)
{
+ spin_lock(&file->f_lock);
file->f_flags |= O_NONBLOCK;
+ spin_unlock(&file->f_lock);
return 0;
}
@@ -1866,7 +1962,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)
return result;
}
-static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr)
+static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t hw_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t appl_ptr;
@@ -1882,7 +1979,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
int err, cmd;
#ifdef OSS_DEBUG
- printk("pcm_oss: trigger = 0x%x\n", trigger);
+ pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger);
#endif
psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
@@ -1902,7 +1999,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
if (runtime->oss.trigger)
goto _skip1;
if (atomic_read(&psubstream->mmap_count))
- snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
+ snd_pcm_oss_simulate_fill(psubstream,
+ get_hw_ptr_period(runtime));
runtime->oss.trigger = 1;
runtime->start_threshold = 1;
cmd = SNDRV_PCM_IOCTL_START;
@@ -2021,11 +2119,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
if (atomic_read(&substream->mmap_count)) {
snd_pcm_sframes_t n;
- n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
+ delay = get_hw_ptr_period(runtime);
+ n = delay - runtime->oss.prev_hw_ptr_period;
if (n < 0)
n += runtime->boundary;
info.blocks = n / runtime->period_size;
- runtime->oss.prev_hw_ptr_interrupt = delay;
+ runtime->oss.prev_hw_ptr_period = delay;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_pcm_oss_simulate_fill(substream, delay);
info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
@@ -2100,7 +2199,9 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
}
#ifdef OSS_DEBUG
- printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n",
+ info.bytes, info.fragments, info.fragstotal, info.fragsize);
#endif
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -2110,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;
}
@@ -2172,6 +2273,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
runtime->oss.params = 1;
runtime->oss.trigger = 1;
runtime->oss.rate = 8000;
+ mutex_init(&runtime->oss.params_lock);
switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
case SNDRV_MINOR_OSS_PCM_8:
runtime->oss.format = AFMT_U8;
@@ -2192,7 +2294,8 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
{
int cidx;
- snd_assert(pcm_oss_file != NULL, return -ENXIO);
+ if (!pcm_oss_file)
+ return 0;
for (cidx = 0; cidx < 2; ++cidx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];
if (substream)
@@ -2211,10 +2314,10 @@ static int snd_pcm_oss_open_file(struct file *file,
int idx, err;
struct snd_pcm_oss_file *pcm_oss_file;
struct snd_pcm_substream *substream;
- unsigned int f_mode = file->f_mode;
+ fmode_t f_mode = file->f_mode;
- snd_assert(rpcm_oss_file != NULL, return -EINVAL);
- *rpcm_oss_file = NULL;
+ if (rpcm_oss_file)
+ *rpcm_oss_file = NULL;
pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL);
if (pcm_oss_file == NULL)
@@ -2254,7 +2357,8 @@ static int snd_pcm_oss_open_file(struct file *file,
}
file->private_data = pcm_oss_file;
- *rpcm_oss_file = pcm_oss_file;
+ if (rpcm_oss_file)
+ *rpcm_oss_file = pcm_oss_file;
return 0;
}
@@ -2263,7 +2367,8 @@ static int snd_task_name(struct task_struct *task, char *name, size_t size)
{
unsigned int idx;
- snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+ if (snd_BUG_ON(!task || !name || size < 2))
+ return -EINVAL;
for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
name[idx] = task->comm[idx];
name[idx] = '\0';
@@ -2280,6 +2385,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
int nonblock;
wait_queue_t wait;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
pcm = snd_lookup_oss_minor_data(iminor(inode),
SNDRV_OSS_DEVICE_TYPE_PCM);
if (pcm == NULL) {
@@ -2328,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;
@@ -2337,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:
@@ -2344,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;
}
@@ -2357,7 +2473,8 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream == NULL)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
pcm = substream->pcm;
if (!pcm->card->shutdown)
snd_pcm_oss_sync(pcm_oss_file);
@@ -2390,14 +2507,15 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (substream != NULL)
break;
}
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(idx >= 2))
+ return -ENXIO;
return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
}
#endif
if (((cmd >> 8) & 0xff) != 'P')
return -EINVAL;
#ifdef OSS_DEBUG
- printk("pcm_oss: ioctl = 0x%x\n", cmd);
+ pr_debug("pcm_oss: ioctl = 0x%x\n", cmd);
#endif
switch (cmd) {
case SNDCTL_DSP_RESET:
@@ -2524,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;
}
@@ -2551,7 +2669,9 @@ static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t coun
#else
{
ssize_t res = snd_pcm_oss_read1(substream, buf, count);
- printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read %li bytes (returned %li bytes)\n",
+ (long)count, (long)res);
return res;
}
#endif
@@ -2570,7 +2690,8 @@ static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size
substream->f_flags = file->f_flags & O_NONBLOCK;
result = snd_pcm_oss_write1(substream, buf, count);
#ifdef OSS_DEBUG
- printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
+ pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n",
+ (long)count, (long)result);
#endif
return result;
}
@@ -2579,18 +2700,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
- return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ return runtime->oss.prev_hw_ptr_period !=
+ get_hw_ptr_period(runtime);
else
- return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
+ return snd_pcm_playback_avail(runtime) >=
+ runtime->oss.period_frames;
}
static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
- return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ return runtime->oss.prev_hw_ptr_period !=
+ get_hw_ptr_period(runtime);
else
- return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
+ return snd_pcm_capture_avail(runtime) >=
+ runtime->oss.period_frames;
}
static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
@@ -2644,7 +2769,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
int err;
#ifdef OSS_DEBUG
- printk("pcm_oss: mmap begin\n");
+ pr_debug("pcm_oss: mmap begin\n");
#endif
pcm_oss_file = file->private_data;
switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
@@ -2694,7 +2819,8 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
runtime->silence_threshold = 0;
runtime->silence_size = 0;
#ifdef OSS_DEBUG
- printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes);
+ pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n",
+ runtime->oss.mmap_bytes);
#endif
/* In mmap mode we never stop */
runtime->stop_threshold = runtime->boundary;
@@ -2746,7 +2872,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_str *pstr = entry->private_data;
- char line[128], str[32], task_name[32], *ptr;
+ char line[128], str[32], task_name[32];
+ const char *ptr;
int idx1;
struct snd_pcm_oss_setup *setup, *setup1, template;
@@ -2796,7 +2923,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
setup = kmalloc(sizeof(*setup), GFP_KERNEL);
if (! setup) {
buffer->error = -ENOMEM;
- mutex_lock(&pstr->oss.setup_mutex);
+ mutex_unlock(&pstr->oss.setup_mutex);
return;
}
if (pstr->oss.setup_list == NULL)
@@ -2810,7 +2937,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
if (! template.task_name) {
kfree(setup);
buffer->error = -ENOMEM;
- mutex_lock(&pstr->oss.setup_mutex);
+ mutex_unlock(&pstr->oss.setup_mutex);
return;
}
}
@@ -2861,13 +2988,14 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
* ENTRY functions
*/
-static struct file_operations snd_pcm_oss_f_reg =
+static const struct file_operations snd_pcm_oss_f_reg =
{
.owner = THIS_MODULE,
.read = snd_pcm_oss_read,
.write = snd_pcm_oss_write,
.open = snd_pcm_oss_open,
.release = snd_pcm_oss_release,
+ .llseek = no_llseek,
.poll = snd_pcm_oss_poll,
.unlocked_ioctl = snd_pcm_oss_ioctl,
.compat_ioctl = snd_pcm_oss_ioctl_compat,
@@ -2876,12 +3004,10 @@ static 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);
}
}
@@ -2962,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 0e67dd280a5..727ac44d39f 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -1,6 +1,6 @@
/*
* PCM Plug-In shared (kernel/library) code
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
*
@@ -24,10 +24,6 @@
#define PLUGIN_DEBUG
#endif
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
@@ -66,7 +62,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
return width;
size = frames * format->channels * width;
- snd_assert((size % 8) == 0, return -ENXIO);
+ if (snd_BUG_ON(size % 8))
+ return -ENXIO;
size /= 8;
if (plugin->buf_frames < frames) {
vfree(plugin->buf);
@@ -88,7 +85,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
c->area.step = format->channels * width;
}
} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
- snd_assert((size % format->channels) == 0,);
+ if (snd_BUG_ON(size % format->channels))
+ return -EINVAL;
size /= format->channels;
for (channel = 0; channel < format->channels; channel++, c++) {
c->frames = frames;
@@ -106,13 +104,15 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
{
int err;
- snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+ if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
+ return -ENXIO;
if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- snd_assert(frames > 0, return -ENXIO);
+ if (snd_BUG_ON(frames <= 0))
+ return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
if (err < 0)
@@ -123,7 +123,8 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->prev) {
if (plugin->src_frames)
frames = plugin->src_frames(plugin, frames);
- snd_assert(frames > 0, return -ENXIO);
+ if (snd_BUG_ON(frames <= 0))
+ return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
if (err < 0)
@@ -152,8 +153,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
unsigned int channels;
- snd_assert(plug != NULL, return -ENXIO);
- snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
+ if (snd_BUG_ON(!src_format || !dst_format))
+ return -ENXIO;
plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
if (plugin == NULL)
return -ENOMEM;
@@ -163,10 +166,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
plugin->src_format = *src_format;
plugin->src_width = snd_pcm_format_physical_width(src_format->format);
- snd_assert(plugin->src_width > 0, );
+ snd_BUG_ON(plugin->src_width <= 0);
plugin->dst_format = *dst_format;
plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
- snd_assert(plugin->dst_width > 0, );
+ snd_BUG_ON(plugin->dst_width <= 0);
if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
channels = src_format->channels;
else
@@ -196,11 +199,13 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
{
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
- int stream = snd_pcm_plug_stream(plug);
+ int stream;
- snd_assert(plug != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
if (drv_frames == 0)
return 0;
+ stream = snd_pcm_plug_stream(plug);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
plugin = snd_pcm_plug_last(plug);
while (plugin && drv_frames > 0) {
@@ -226,12 +231,14 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
{
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
snd_pcm_sframes_t frames;
- int stream = snd_pcm_plug_stream(plug);
+ int stream;
- snd_assert(plug != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
if (clt_frames == 0)
return 0;
frames = clt_frames;
+ stream = snd_pcm_plug_stream(plug);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) {
@@ -259,7 +266,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
return frames;
}
-static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
+static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format)
{
struct snd_mask formats = *mask;
u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
@@ -267,22 +274,28 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
+ 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,
SNDRV_PCM_FORMAT_U16_BE,
+ SNDRV_PCM_FORMAT_S24_3LE,
+ SNDRV_PCM_FORMAT_S24_3BE,
+ SNDRV_PCM_FORMAT_U24_3LE,
+ SNDRV_PCM_FORMAT_U24_3BE,
SNDRV_PCM_FORMAT_S24_LE,
SNDRV_PCM_FORMAT_S24_BE,
SNDRV_PCM_FORMAT_U24_LE,
@@ -295,52 +308,52 @@ 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)
{
- if (snd_mask_test(format_mask, format))
+ int i;
+
+ 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)) {
- int width = snd_pcm_format_width(format);
- int unsignd = snd_pcm_format_unsigned(format);
- int big = snd_pcm_format_big_endian(format);
- int format1;
- int wid, width1=width;
- int dwidth1 = 8;
- for (wid = 0; wid < 4; ++wid) {
- int end, big1 = big;
- for (end = 0; end < 2; ++end) {
- int sgn, unsignd1 = unsignd;
- for (sgn = 0; sgn < 2; ++sgn) {
- format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
- if (format1 >= 0 &&
- snd_mask_test(format_mask, format1))
- goto _found;
- unsignd1 = !unsignd1;
- }
- big1 = !big1;
- }
- if (width1 == 32) {
- dwidth1 = -dwidth1;
- width1 = width;
+ 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;
+ snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
+ for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
+ snd_pcm_format_t f = preferred_formats[i];
+ unsigned int w;
+ if (!snd_mask_test(format_mask, (__force int)f))
+ continue;
+ w = snd_pcm_format_width(f);
+ if (w >= width)
+ badness = w - width;
+ else
+ badness = width - w + 32;
+ badness += snd_pcm_format_unsigned(f) != unsignd;
+ badness += snd_pcm_format_big_endian(f) != big;
+ if (badness < best) {
+ best_format = f;
+ best = badness;
}
- width1 += dwidth1;
}
- return -EINVAL;
- _found:
- return format1;
+ if ((__force int)best_format >= 0)
+ return best_format;
+ else
+ return (__force snd_pcm_format_t)-EINVAL;
} else {
- unsigned int i;
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;
}
}
}
@@ -352,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);
@@ -542,7 +555,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
int width, nchannels, channel;
int stream = snd_pcm_plug_stream(plug);
- snd_assert(buf != NULL, return -ENXIO);
+ if (snd_BUG_ON(!buf))
+ return -ENXIO;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
plugin = snd_pcm_plug_first(plug);
format = &plugin->src_format;
@@ -555,7 +569,9 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
return width;
nchannels = format->channels;
- snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+ if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+ format->channels > 1))
+ return -ENXIO;
for (channel = 0; channel < nchannels; channel++, v++) {
v->frames = count;
v->enabled = 1;
@@ -631,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;
@@ -678,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;
@@ -740,5 +756,3 @@ int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_of
}
return 0;
}
-
-#endif
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index 3be91b3d537..a5035c2369a 100644
--- a/sound/core/oss/pcm_plugin.h
+++ b/sound/core/oss/pcm_plugin.h
@@ -3,7 +3,7 @@
/*
* Digital Audio (Plugin interface) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel {
};
struct snd_pcm_plugin_format {
- int format;
+ snd_pcm_format_t format;
unsigned int rate;
unsigned int channels;
};
@@ -58,7 +58,7 @@ struct snd_pcm_plugin {
struct snd_pcm_plugin_format dst_format; /* destination format */
int src_width; /* sample width in bits */
int dst_width; /* sample width in bits */
- int access;
+ snd_pcm_access_t access;
snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames);
snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames);
snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin,
@@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_pcm_hw_params *slave_params);
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask);
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+ struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
@@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel,
size_t src_offset,
const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size);
void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr);
@@ -176,9 +177,9 @@ static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_
#endif
#ifdef PLUGIN_DEBUG
-#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
+#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args)
#else
-#define pdprintf( fmt, args... )
+#define pdprintf(fmt, args...)
#endif
#endif /* __PCM_PLUGIN_H */
diff --git a/sound/core/oss/plugin_ops.h b/sound/core/oss/plugin_ops.h
deleted file mode 100644
index 1f5bde4631f..00000000000
--- a/sound/core/oss/plugin_ops.h
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Plugin sample operators with fast switch
- * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
- *
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-
-#define as_u8(ptr) (*(u_int8_t*)(ptr))
-#define as_u16(ptr) (*(u_int16_t*)(ptr))
-#define as_u32(ptr) (*(u_int32_t*)(ptr))
-#define as_u64(ptr) (*(u_int64_t*)(ptr))
-#define as_s8(ptr) (*(int8_t*)(ptr))
-#define as_s16(ptr) (*(int16_t*)(ptr))
-#define as_s32(ptr) (*(int32_t*)(ptr))
-#define as_s64(ptr) (*(int64_t*)(ptr))
-
-#ifdef COPY_LABELS
-static void *copy_labels[4] = {
- &&copy_8,
- &&copy_16,
- &&copy_32,
- &&copy_64
-};
-#endif
-
-#ifdef COPY_END
-while(0) {
-copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
-copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
-copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
-copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
-}
-#endif
-
-#ifdef CONV_LABELS
-/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
-static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
- &&conv_xxx1_xxx1, /* 8h -> 8h */
- &&conv_xxx1_xxx1, /* 8h -> 8s */
- &&conv_xxx1_xx10, /* 8h -> 16h */
- &&conv_xxx1_xx01, /* 8h -> 16s */
- &&conv_xxx1_x100, /* 8h -> 24h */
- &&conv_xxx1_001x, /* 8h -> 24s */
- &&conv_xxx1_1000, /* 8h -> 32h */
- &&conv_xxx1_0001, /* 8h -> 32s */
- &&conv_xxx1_xxx9, /* 8h ^> 8h */
- &&conv_xxx1_xxx9, /* 8h ^> 8s */
- &&conv_xxx1_xx90, /* 8h ^> 16h */
- &&conv_xxx1_xx09, /* 8h ^> 16s */
- &&conv_xxx1_x900, /* 8h ^> 24h */
- &&conv_xxx1_009x, /* 8h ^> 24s */
- &&conv_xxx1_9000, /* 8h ^> 32h */
- &&conv_xxx1_0009, /* 8h ^> 32s */
- &&conv_xxx1_xxx1, /* 8s -> 8h */
- &&conv_xxx1_xxx1, /* 8s -> 8s */
- &&conv_xxx1_xx10, /* 8s -> 16h */
- &&conv_xxx1_xx01, /* 8s -> 16s */
- &&conv_xxx1_x100, /* 8s -> 24h */
- &&conv_xxx1_001x, /* 8s -> 24s */
- &&conv_xxx1_1000, /* 8s -> 32h */
- &&conv_xxx1_0001, /* 8s -> 32s */
- &&conv_xxx1_xxx9, /* 8s ^> 8h */
- &&conv_xxx1_xxx9, /* 8s ^> 8s */
- &&conv_xxx1_xx90, /* 8s ^> 16h */
- &&conv_xxx1_xx09, /* 8s ^> 16s */
- &&conv_xxx1_x900, /* 8s ^> 24h */
- &&conv_xxx1_009x, /* 8s ^> 24s */
- &&conv_xxx1_9000, /* 8s ^> 32h */
- &&conv_xxx1_0009, /* 8s ^> 32s */
- &&conv_xx12_xxx1, /* 16h -> 8h */
- &&conv_xx12_xxx1, /* 16h -> 8s */
- &&conv_xx12_xx12, /* 16h -> 16h */
- &&conv_xx12_xx21, /* 16h -> 16s */
- &&conv_xx12_x120, /* 16h -> 24h */
- &&conv_xx12_021x, /* 16h -> 24s */
- &&conv_xx12_1200, /* 16h -> 32h */
- &&conv_xx12_0021, /* 16h -> 32s */
- &&conv_xx12_xxx9, /* 16h ^> 8h */
- &&conv_xx12_xxx9, /* 16h ^> 8s */
- &&conv_xx12_xx92, /* 16h ^> 16h */
- &&conv_xx12_xx29, /* 16h ^> 16s */
- &&conv_xx12_x920, /* 16h ^> 24h */
- &&conv_xx12_029x, /* 16h ^> 24s */
- &&conv_xx12_9200, /* 16h ^> 32h */
- &&conv_xx12_0029, /* 16h ^> 32s */
- &&conv_xx12_xxx2, /* 16s -> 8h */
- &&conv_xx12_xxx2, /* 16s -> 8s */
- &&conv_xx12_xx21, /* 16s -> 16h */
- &&conv_xx12_xx12, /* 16s -> 16s */
- &&conv_xx12_x210, /* 16s -> 24h */
- &&conv_xx12_012x, /* 16s -> 24s */
- &&conv_xx12_2100, /* 16s -> 32h */
- &&conv_xx12_0012, /* 16s -> 32s */
- &&conv_xx12_xxxA, /* 16s ^> 8h */
- &&conv_xx12_xxxA, /* 16s ^> 8s */
- &&conv_xx12_xxA1, /* 16s ^> 16h */
- &&conv_xx12_xx1A, /* 16s ^> 16s */
- &&conv_xx12_xA10, /* 16s ^> 24h */
- &&conv_xx12_01Ax, /* 16s ^> 24s */
- &&conv_xx12_A100, /* 16s ^> 32h */
- &&conv_xx12_001A, /* 16s ^> 32s */
- &&conv_x123_xxx1, /* 24h -> 8h */
- &&conv_x123_xxx1, /* 24h -> 8s */
- &&conv_x123_xx12, /* 24h -> 16h */
- &&conv_x123_xx21, /* 24h -> 16s */
- &&conv_x123_x123, /* 24h -> 24h */
- &&conv_x123_321x, /* 24h -> 24s */
- &&conv_x123_1230, /* 24h -> 32h */
- &&conv_x123_0321, /* 24h -> 32s */
- &&conv_x123_xxx9, /* 24h ^> 8h */
- &&conv_x123_xxx9, /* 24h ^> 8s */
- &&conv_x123_xx92, /* 24h ^> 16h */
- &&conv_x123_xx29, /* 24h ^> 16s */
- &&conv_x123_x923, /* 24h ^> 24h */
- &&conv_x123_329x, /* 24h ^> 24s */
- &&conv_x123_9230, /* 24h ^> 32h */
- &&conv_x123_0329, /* 24h ^> 32s */
- &&conv_123x_xxx3, /* 24s -> 8h */
- &&conv_123x_xxx3, /* 24s -> 8s */
- &&conv_123x_xx32, /* 24s -> 16h */
- &&conv_123x_xx23, /* 24s -> 16s */
- &&conv_123x_x321, /* 24s -> 24h */
- &&conv_123x_123x, /* 24s -> 24s */
- &&conv_123x_3210, /* 24s -> 32h */
- &&conv_123x_0123, /* 24s -> 32s */
- &&conv_123x_xxxB, /* 24s ^> 8h */
- &&conv_123x_xxxB, /* 24s ^> 8s */
- &&conv_123x_xxB2, /* 24s ^> 16h */
- &&conv_123x_xx2B, /* 24s ^> 16s */
- &&conv_123x_xB21, /* 24s ^> 24h */
- &&conv_123x_12Bx, /* 24s ^> 24s */
- &&conv_123x_B210, /* 24s ^> 32h */
- &&conv_123x_012B, /* 24s ^> 32s */
- &&conv_1234_xxx1, /* 32h -> 8h */
- &&conv_1234_xxx1, /* 32h -> 8s */
- &&conv_1234_xx12, /* 32h -> 16h */
- &&conv_1234_xx21, /* 32h -> 16s */
- &&conv_1234_x123, /* 32h -> 24h */
- &&conv_1234_321x, /* 32h -> 24s */
- &&conv_1234_1234, /* 32h -> 32h */
- &&conv_1234_4321, /* 32h -> 32s */
- &&conv_1234_xxx9, /* 32h ^> 8h */
- &&conv_1234_xxx9, /* 32h ^> 8s */
- &&conv_1234_xx92, /* 32h ^> 16h */
- &&conv_1234_xx29, /* 32h ^> 16s */
- &&conv_1234_x923, /* 32h ^> 24h */
- &&conv_1234_329x, /* 32h ^> 24s */
- &&conv_1234_9234, /* 32h ^> 32h */
- &&conv_1234_4329, /* 32h ^> 32s */
- &&conv_1234_xxx4, /* 32s -> 8h */
- &&conv_1234_xxx4, /* 32s -> 8s */
- &&conv_1234_xx43, /* 32s -> 16h */
- &&conv_1234_xx34, /* 32s -> 16s */
- &&conv_1234_x432, /* 32s -> 24h */
- &&conv_1234_234x, /* 32s -> 24s */
- &&conv_1234_4321, /* 32s -> 32h */
- &&conv_1234_1234, /* 32s -> 32s */
- &&conv_1234_xxxC, /* 32s ^> 8h */
- &&conv_1234_xxxC, /* 32s ^> 8s */
- &&conv_1234_xxC3, /* 32s ^> 16h */
- &&conv_1234_xx3C, /* 32s ^> 16s */
- &&conv_1234_xC32, /* 32s ^> 24h */
- &&conv_1234_23Cx, /* 32s ^> 24s */
- &&conv_1234_C321, /* 32s ^> 32h */
- &&conv_1234_123C, /* 32s ^> 32s */
-};
-#endif
-
-#ifdef CONV_END
-while(0) {
-conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
-conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
-conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
-conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
-conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
-conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
-conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
-conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
-conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
-conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
-conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
-conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
-conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
-conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
-conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
-conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
-conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
-conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
-conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
-conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
-conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
-conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
-conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
-conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
-conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
-conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
-conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
-conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
-conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
-conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
-conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
-conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
-conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
-conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
-conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
-conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
-conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
-conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
-conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
-conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
-conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
-conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
-conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
-conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
-conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
-conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
-conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
-conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
-conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
-conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
-conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
-conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
-conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
-conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
-conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
-conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
-conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
-conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
-conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
-conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
-conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
-conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
-conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
-conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
-conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
-conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
-conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
-conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
-conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
-conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
-conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
-conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
-conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
-conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
-conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
-conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
-conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
-conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
-conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
-conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
-conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
-conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
-conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
-conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
-conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
-conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
-conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
-conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
-conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
-conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
-conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
-conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
-conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
-conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
-}
-#endif
-
-#ifdef GET_S16_LABELS
-/* src_wid src_endswap unsigned */
-static void *get_s16_labels[4 * 2 * 2] = {
- &&get_s16_xxx1_xx10, /* 8h -> 16h */
- &&get_s16_xxx1_xx90, /* 8h ^> 16h */
- &&get_s16_xxx1_xx10, /* 8s -> 16h */
- &&get_s16_xxx1_xx90, /* 8s ^> 16h */
- &&get_s16_xx12_xx12, /* 16h -> 16h */
- &&get_s16_xx12_xx92, /* 16h ^> 16h */
- &&get_s16_xx12_xx21, /* 16s -> 16h */
- &&get_s16_xx12_xxA1, /* 16s ^> 16h */
- &&get_s16_x123_xx12, /* 24h -> 16h */
- &&get_s16_x123_xx92, /* 24h ^> 16h */
- &&get_s16_123x_xx32, /* 24s -> 16h */
- &&get_s16_123x_xxB2, /* 24s ^> 16h */
- &&get_s16_1234_xx12, /* 32h -> 16h */
- &&get_s16_1234_xx92, /* 32h ^> 16h */
- &&get_s16_1234_xx43, /* 32s -> 16h */
- &&get_s16_1234_xxC3, /* 32s ^> 16h */
-};
-#endif
-
-#ifdef GET_S16_END
-while(0) {
-get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
-get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
-get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
-get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
-get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
-get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
-get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
-get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
-get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
-get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
-get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
-get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
-get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
-get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
-}
-#endif
-
-#ifdef PUT_S16_LABELS
-/* dst_wid dst_endswap unsigned */
-static void *put_s16_labels[4 * 2 * 2] = {
- &&put_s16_xx12_xxx1, /* 16h -> 8h */
- &&put_s16_xx12_xxx9, /* 16h ^> 8h */
- &&put_s16_xx12_xxx1, /* 16h -> 8s */
- &&put_s16_xx12_xxx9, /* 16h ^> 8s */
- &&put_s16_xx12_xx12, /* 16h -> 16h */
- &&put_s16_xx12_xx92, /* 16h ^> 16h */
- &&put_s16_xx12_xx21, /* 16h -> 16s */
- &&put_s16_xx12_xx29, /* 16h ^> 16s */
- &&put_s16_xx12_x120, /* 16h -> 24h */
- &&put_s16_xx12_x920, /* 16h ^> 24h */
- &&put_s16_xx12_021x, /* 16h -> 24s */
- &&put_s16_xx12_029x, /* 16h ^> 24s */
- &&put_s16_xx12_1200, /* 16h -> 32h */
- &&put_s16_xx12_9200, /* 16h ^> 32h */
- &&put_s16_xx12_0021, /* 16h -> 32s */
- &&put_s16_xx12_0029, /* 16h ^> 32s */
-};
-#endif
-
-#ifdef PUT_S16_END
-while (0) {
-put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
-put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
-put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
-put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
-put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
-put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
-put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
-put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
-put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
-put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
-put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
-put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
-put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
-put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
-}
-#endif
-
-#undef as_u8
-#undef as_u16
-#undef as_u32
-#undef as_s8
-#undef as_s16
-#undef as_s32
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 18d8a0f4e81..2fa9299a440 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -1,6 +1,6 @@
/*
* Rate conversion Plug-In
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
*
* This library is free software; you can redistribute it and/or modify
@@ -19,10 +19,6 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -161,7 +157,7 @@ static void resample_shrink(struct snd_pcm_plugin *plugin,
while (dst_frames1 > 0) {
S1 = S2;
if (src_frames1-- > 0) {
- S1 = *src;
+ S2 = *src;
src += src_step;
}
if (pos & ~R_MASK) {
@@ -189,7 +185,8 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_
struct rate_priv *data;
snd_pcm_sframes_t res;
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
if (frames == 0)
return 0;
data = (struct rate_priv *)plugin->extra_data;
@@ -221,7 +218,8 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_
struct rate_priv *data;
snd_pcm_sframes_t res;
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
if (frames == 0)
return 0;
data = (struct rate_priv *)plugin->extra_data;
@@ -256,19 +254,20 @@ static snd_pcm_sframes_t rate_transfer(struct snd_pcm_plugin *plugin,
snd_pcm_uframes_t dst_frames;
struct rate_priv *data;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
{
unsigned int channel;
for (channel = 0; channel < plugin->src_format.channels; channel++) {
- snd_assert(src_channels[channel].area.first % 8 == 0 &&
- src_channels[channel].area.step % 8 == 0,
- return -ENXIO);
- snd_assert(dst_channels[channel].area.first % 8 == 0 &&
- dst_channels[channel].area.step % 8 == 0,
- return -ENXIO);
+ if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+ src_channels[channel].area.step % 8))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+ dst_channels[channel].area.step % 8))
+ return -ENXIO;
}
}
#endif
@@ -285,7 +284,8 @@ static int rate_action(struct snd_pcm_plugin *plugin,
enum snd_pcm_plugin_action action,
unsigned long udata)
{
- snd_assert(plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin))
+ return -ENXIO;
switch (action) {
case INIT:
case PREPARE:
@@ -306,14 +306,20 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
struct rate_priv *data;
struct snd_pcm_plugin *plugin;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
- snd_assert(src_format->channels > 0, return -ENXIO);
- snd_assert(src_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
- snd_assert(dst_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
- snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+ if (snd_BUG_ON(src_format->channels != dst_format->channels))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->channels <= 0))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16))
+ return -ENXIO;
+ if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->rate == dst_format->rate))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "rate conversion",
src_format, dst_format,
@@ -340,5 +346,3 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index 46917dc0196..c8171f5783c 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -19,18 +19,13 @@
*
*/
-#include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
-#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "pcm_plugin.h"
static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
- snd_pcm_uframes_t frames, int format)
+ snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
int dst = 0;
for (; dst < ndsts; ++dst) {
@@ -43,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);
@@ -56,9 +51,10 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
{
int nsrcs, ndsts, dst;
struct snd_pcm_plugin_channel *dvp;
- int format;
+ snd_pcm_format_t format;
- snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+ return -ENXIO;
if (frames == 0)
return 0;
@@ -94,10 +90,13 @@ int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug,
struct snd_pcm_plugin *plugin;
int err;
- snd_assert(r_plugin != NULL, return -ENXIO);
+ if (snd_BUG_ON(!r_plugin))
+ return -ENXIO;
*r_plugin = NULL;
- snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
- snd_assert(src_format->format == dst_format->format, return -ENXIO);
+ if (snd_BUG_ON(src_format->rate != dst_format->rate))
+ return -ENXIO;
+ if (snd_BUG_ON(src_format->format != dst_format->format))
+ return -ENXIO;
err = snd_pcm_plugin_build(plug, "route conversion",
src_format, dst_format, 0, &plugin);
@@ -108,5 +107,3 @@ int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug,
*r_plugin = plugin;
return 0;
}
-
-#endif
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 5ac6e19ccb4..43932e8dce6 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,18 +19,19 @@
*
*/
-#include <sound/driver.h>
#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>
#include <sound/control.h>
#include <sound/info.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
MODULE_LICENSE("GPL");
@@ -43,19 +44,52 @@ static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device);
-static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
+static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_pcm *pcm;
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->internal)
+ continue;
if (pcm->card == card && pcm->device == device)
return pcm;
}
return NULL;
}
+static int snd_pcm_next(struct snd_card *card, int device)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->internal)
+ continue;
+ if (pcm->card == card && pcm->device > device)
+ return pcm->device;
+ else if (pcm->card->number > card->number)
+ return -1;
+ }
+ return -1;
+}
+
+static int snd_pcm_add(struct snd_pcm *newpcm)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->card == newpcm->card && pcm->device == newpcm->device)
+ return -EBUSY;
+ if (pcm->card->number > newpcm->card->number ||
+ (pcm->card == newpcm->card &&
+ pcm->device > newpcm->device)) {
+ list_add(&newpcm->list, pcm->list.prev);
+ return 0;
+ }
+ }
+ list_add_tail(&newpcm->list, &snd_pcm_devices);
+ return 0;
+}
+
static int snd_pcm_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd, unsigned long arg)
@@ -68,14 +102,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
mutex_lock(&register_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_PCM_DEVICES) {
- if (snd_pcm_search(card, device))
- break;
- device++;
- }
- if (device == SNDRV_PCM_DEVICES)
- device = -1;
+ device = snd_pcm_next(card, device);
mutex_unlock(&register_mutex);
if (put_user(device, (int __user *)arg))
return -EFAULT;
@@ -101,7 +128,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
mutex_lock(&register_mutex);
- pcm = snd_pcm_search(card, device);
+ pcm = snd_pcm_get(card, device);
if (pcm == NULL) {
err = -ENXIO;
goto _error;
@@ -141,18 +168,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
return -ENOIOCTLCMD;
}
-#ifdef CONFIG_SND_VERBOSE_PROCFS
-
-#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
-#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
-#define READY(v) [SNDRV_PCM_READY_##v] = #v
-#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
-#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
-#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
-#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
-#define START(v) [SNDRV_PCM_START_##v] = #v
#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
static char *snd_pcm_format_names[] = {
FORMAT(S8),
@@ -193,12 +209,33 @@ static char *snd_pcm_format_names[] = {
FORMAT(S18_3BE),
FORMAT(U18_3LE),
FORMAT(U18_3BE),
+ FORMAT(G723_24),
+ FORMAT(G723_24_1B),
+ FORMAT(G723_40),
+ FORMAT(G723_40_1B),
+ FORMAT(DSD_U8),
+ FORMAT(DSD_U16_LE),
};
-static const char *snd_pcm_format_name(snd_pcm_format_t format)
+const char *snd_pcm_format_name(snd_pcm_format_t format)
{
- return snd_pcm_format_names[format];
+ if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
+ return "Unknown";
+ return snd_pcm_format_names[(__force unsigned int)format];
}
+EXPORT_SYMBOL_GPL(snd_pcm_format_name);
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+
+#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
+#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
+#define READY(v) [SNDRV_PCM_READY_##v] = #v
+#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
+#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
+#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
+#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
static char *snd_pcm_stream_names[] = {
STREAM(PLAYBACK),
@@ -230,37 +267,35 @@ static char *snd_pcm_subformat_names[] = {
static char *snd_pcm_tstamp_mode_names[] = {
TSTAMP(NONE),
- TSTAMP(MMAP),
+ TSTAMP(ENABLE),
};
static const char *snd_pcm_stream_name(int stream)
{
- snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);
return snd_pcm_stream_names[stream];
}
static const char *snd_pcm_access_name(snd_pcm_access_t access)
{
- return snd_pcm_access_names[access];
+ return snd_pcm_access_names[(__force int)access];
}
static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
{
- return snd_pcm_subformat_names[subformat];
+ return snd_pcm_subformat_names[(__force int)subformat];
}
static const char *snd_pcm_tstamp_mode_name(int mode)
{
- snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);
return snd_pcm_tstamp_mode_names[mode];
}
static const char *snd_pcm_state_name(snd_pcm_state_t state)
{
- return snd_pcm_state_names[state];
+ return snd_pcm_state_names[(__force int)state];
}
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
#include <linux/soundcard.h>
static const char *snd_pcm_oss_format_name(int format)
@@ -303,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;
}
@@ -337,22 +373,24 @@ static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data,
- buffer);
+ snd_pcm_proc_info_read(entry->private_data, buffer);
}
static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- return;
+ goto unlock;
}
snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
@@ -361,8 +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);
- snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
-#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);
@@ -372,51 +409,60 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- return;
+ goto unlock;
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
- snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
- snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
struct snd_pcm_status status;
int err;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
memset(&status, 0, sizeof(status));
err = snd_pcm_status(substream, &status);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
- return;
+ goto unlock;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+ snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
@@ -427,6 +473,8 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "-----\n");
snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr);
snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr);
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -596,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)
{
@@ -604,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;
}
}
@@ -621,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;
@@ -629,25 +677,29 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
- snprintf(substream->latency_id, sizeof(substream->latency_id),
- "ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device,
- (stream ? 'c' : 'p'), idx);
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL)
pstr->substream = substream;
else
prev->next = substream;
- err = snd_pcm_substream_proc_init(substream);
- if (err < 0) {
- snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
- kfree(substream);
- return err;
+
+ if (!pcm->internal) {
+ err = snd_pcm_substream_proc_init(substream);
+ if (err < 0) {
+ pcm_err(pcm,
+ "Error in snd_pcm_stream_proc_init\n");
+ if (prev == NULL)
+ pstr->substream = NULL;
+ else
+ prev->next = NULL;
+ kfree(substream);
+ return err;
+ }
}
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
- spin_lock_init(&substream->timer_lock);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
@@ -656,25 +708,9 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
EXPORT_SYMBOL(snd_pcm_new_stream);
-/**
- * snd_pcm_new - create a new PCM instance
- * @card: the card instance
- * @id: the id string
- * @device: the device index (zero based)
- * @playback_count: the number of substreams for playback
- * @capture_count: the number of substreams for capture
- * @rpcm: the pointer to store the new pcm instance
- *
- * Creates a new PCM instance.
- *
- * The pcm operators have to be set afterwards to the new instance
- * via snd_pcm_set_ops().
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_pcm_new(struct snd_card *card, char *id, int device,
- int playback_count, int capture_count,
- struct snd_pcm ** rpcm)
+static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, bool internal,
+ struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
@@ -684,16 +720,18 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_pcm_dev_disconnect,
};
- snd_assert(rpcm != NULL, return -EINVAL);
- *rpcm = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rpcm)
+ *rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
- snd_printk(KERN_ERR "Cannot allocate PCM\n");
+ dev_err(card->dev, "Cannot allocate PCM\n");
return -ENOMEM;
}
pcm->card = card;
pcm->device = device;
+ pcm->internal = internal;
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
@@ -710,16 +748,68 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
snd_pcm_free(pcm);
return err;
}
- *rpcm = pcm;
+ if (rpcm)
+ *rpcm = pcm;
return 0;
}
+/**
+ * snd_pcm_new - create a new PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new PCM instance.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ false, rpcm);
+}
EXPORT_SYMBOL(snd_pcm_new);
+/**
+ * snd_pcm_new_internal - create a new internal PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based - shared with normal PCMs)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new internal PCM instance with no userspace device or procfs
+ * entries. This is used by ASoC Back End PCMs in order to create a PCM that
+ * will only be used internally by kernel drivers. i.e. it cannot be opened
+ * by userspace. It provides existing ASoC components drivers with a substream
+ * and access to any private data.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count,
+ struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ true, rpcm);
+}
+EXPORT_SYMBOL(snd_pcm_new_internal);
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss_setup *setup, *setupn;
#endif
substream = pstr->substream;
@@ -731,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);
@@ -744,7 +834,8 @@ static int snd_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_notify *notify;
- snd_assert(pcm != NULL, return -ENXIO);
+ if (!pcm)
+ return 0;
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_unregister(pcm);
}
@@ -763,12 +854,6 @@ static int snd_pcm_dev_free(struct snd_device *device)
return snd_pcm_free(pcm);
}
-static void snd_pcm_tick_timer_func(unsigned long data)
-{
- struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data;
- snd_pcm_tick_elapsed(substream);
-}
-
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
@@ -778,28 +863,26 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct snd_pcm_runtime *runtime;
struct snd_ctl_file *kctl;
struct snd_card *card;
- struct list_head *list;
int prefer_subdevice = -1;
size_t size;
- snd_assert(rsubstream != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pcm || !rsubstream))
+ return -ENXIO;
*rsubstream = NULL;
- snd_assert(pcm != NULL, return -ENXIO);
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
- down_read(&card->controls_rwsem);
- list_for_each(list, &card->ctl_files) {
- kctl = snd_ctl_file(list);
- if (kctl->pid == current->pid) {
+ read_lock(&card->ctl_files_rwlock);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
+ if (kctl->pid == task_pid(current)) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
break;
}
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
@@ -877,9 +960,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
memset((void*)runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
- init_timer(&runtime->tick_timer);
- runtime->tick_timer.function = snd_pcm_tick_timer_func;
- runtime->tick_timer.data = (unsigned long) substream;
+ init_waitqueue_head(&runtime->tsleep);
runtime->status->state = SNDRV_PCM_STATE_OPEN;
@@ -887,6 +968,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
substream->private_data = pcm->private_data;
substream->ref_count = 1;
substream->f_flags = file->f_flags;
+ substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
return 0;
@@ -896,8 +978,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
if (runtime->private_free != NULL)
runtime->private_free(runtime);
snd_free_pages((void*)runtime->status,
@@ -905,8 +988,13 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
snd_free_pages((void*)runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ kfree(runtime->hwptr_log);
+#endif
kfree(runtime);
substream->runtime = NULL;
+ put_pid(substream->pid);
+ substream->pid = NULL;
substream->pstr->substream_opened--;
}
@@ -930,27 +1018,42 @@ 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)
{
int cidx, err;
struct snd_pcm_substream *substream;
- struct list_head *list;
+ struct snd_pcm_notify *notify;
char str[16];
- struct snd_pcm *pcm = device->device_data;
+ struct snd_pcm *pcm;
+ struct device *dev;
- snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+ if (snd_BUG_ON(!device || !device->device_data))
+ return -ENXIO;
+ pcm = device->device_data;
mutex_lock(&register_mutex);
- if (snd_pcm_search(pcm->card, pcm->device)) {
+ err = snd_pcm_add(pcm);
+ if (err) {
mutex_unlock(&register_mutex);
- return -EBUSY;
+ return err;
}
- list_add_tail(&pcm->list, &snd_pcm_devices);
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
- if (pcm->streams[cidx].substream == NULL)
+ if (pcm->streams[cidx].substream == NULL || pcm->internal)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
@@ -962,25 +1065,41 @@ static int snd_pcm_dev_register(struct snd_device *device)
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
- if ((err = snd_register_device(devtype, pcm->card,
- pcm->device,
- &snd_pcm_f_ops[cidx],
- pcm, str)) < 0)
- {
+ /* device pointer to use, pcm->dev takes precedence if
+ * it is assigned, otherwise fall back to card's device
+ * if possible */
+ dev = pcm->dev;
+ if (!dev)
+ dev = snd_card_get_device_link(pcm->card);
+ /* register pcm */
+ err = snd_register_device_for_dev(devtype, pcm->card,
+ pcm->device,
+ &snd_pcm_f_ops[cidx],
+ pcm, str, dev);
+ if (err < 0) {
list_del(&pcm->list);
mutex_unlock(&register_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);
}
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
+
+ list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
- }
+
mutex_unlock(&register_mutex);
return 0;
}
@@ -996,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);
}
@@ -1015,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(&register_mutex);
return 0;
@@ -1023,22 +1155,22 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
- struct list_head *p;
+ struct snd_pcm *pcm;
- snd_assert(notify != NULL &&
- notify->n_register != NULL &&
- notify->n_unregister != NULL &&
- notify->n_disconnect, return -EINVAL);
+ if (snd_BUG_ON(!notify ||
+ !notify->n_register ||
+ !notify->n_unregister ||
+ !notify->n_disconnect))
+ return -EINVAL;
mutex_lock(&register_mutex);
if (nfree) {
list_del(&notify->list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_unregister(list_entry(p,
- struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_unregister(pcm);
} else {
list_add_tail(&notify->list, &snd_pcm_notify_list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_register(list_entry(p, struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_register(pcm);
}
mutex_unlock(&register_mutex);
return 0;
@@ -1054,12 +1186,10 @@ EXPORT_SYMBOL(snd_pcm_notify);
static void snd_pcm_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *p;
struct snd_pcm *pcm;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
snd_iprintf(buffer, "%02i-%02i: %s : %s",
pcm->card->number, pcm->device, pcm->id, pcm->name);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 2b539799d23..af49721ba0e 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -21,6 +21,7 @@
/* This file included from pcm_native.c */
#include <linux/compat.h>
+#include <linux/slab.h>
static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
s32 __user *src)
@@ -189,7 +190,9 @@ struct snd_pcm_status32 {
u32 avail_max;
u32 overrange;
s32 suspended_state;
- unsigned char reserved[60];
+ u32 reserved_alignment;
+ struct compat_timespec audio_tstamp;
+ unsigned char reserved[56-sizeof(struct compat_timespec)];
} __attribute__((packed));
@@ -204,17 +207,16 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
return err;
if (put_user(status.state, &src->state) ||
- put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
- put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
- put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
+ compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
+ compat_put_timespec(&status.tstamp, &src->tstamp) ||
put_user(status.appl_ptr, &src->appl_ptr) ||
put_user(status.hw_ptr, &src->hw_ptr) ||
put_user(status.delay, &src->delay) ||
put_user(status.avail, &src->avail) ||
put_user(status.avail_max, &src->avail_max) ||
put_user(status.overrange, &src->overrange) ||
- put_user(status.suspended_state, &src->suspended_state))
+ put_user(status.suspended_state, &src->suspended_state) ||
+ compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
return -EFAULT;
return err;
@@ -232,14 +234,11 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
if (! (runtime = substream->runtime))
return -ENOTTY;
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
/* only fifo_size is different, so just copy all */
- if (copy_from_user(data, data32, sizeof(*data32))) {
- err = -EFAULT;
- goto error;
- }
+ data = memdup_user(data32, sizeof(*data32));
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
if (refine)
err = snd_pcm_hw_refine(substream, data);
else
@@ -344,7 +343,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
kfree(bufs);
return -EFAULT;
}
- bufs[ch] = compat_ptr(ptr);
+ bufs[i] = compat_ptr(ptr);
bufptr++;
}
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -366,6 +365,7 @@ struct snd_pcm_mmap_status32 {
u32 hw_ptr;
struct compat_timespec tstamp;
s32 suspended_state;
+ struct compat_timespec audio_tstamp;
} __attribute__((packed));
struct snd_pcm_mmap_control32 {
@@ -397,7 +397,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
snd_pcm_uframes_t boundary;
int err;
- snd_assert(runtime, return -EINVAL);
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
if (get_user(sflags, &src->flags) ||
get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
@@ -427,12 +428,14 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
sstatus.hw_ptr = status->hw_ptr % boundary;
sstatus.tstamp = status->tstamp;
sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
+ compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+ compat_put_timespec(&sstatus.audio_tstamp,
+ &src->s.status.audio_tstamp) ||
put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
put_user(scontrol.avail_min, &src->c.control.avail_min))
return -EFAULT;
@@ -484,6 +487,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_PVERSION:
case SNDRV_PCM_IOCTL_INFO:
case SNDRV_PCM_IOCTL_TSTAMP:
+ case SNDRV_PCM_IOCTL_TTSTAMP:
case SNDRV_PCM_IOCTL_HWSYNC:
case SNDRV_PCM_IOCTL_PREPARE:
case SNDRV_PCM_IOCTL_RESET:
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 0bb142a2853..9acc77eae48 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.org>
*
*
@@ -20,11 +20,13 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -67,6 +69,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
} else {
if (new_hw_ptr == ULONG_MAX) { /* initialization */
snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
+ if (avail > runtime->buffer_size)
+ avail = runtime->buffer_size;
runtime->silence_filled = avail > 0 ? avail : 0;
runtime->silence_start = (runtime->status->hw_ptr +
runtime->silence_filled) %
@@ -79,19 +83,18 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
runtime->silence_filled -= frames;
if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
runtime->silence_filled = 0;
- runtime->silence_start = (ofs + frames) - runtime->buffer_size;
+ runtime->silence_start = new_hw_ptr;
} else {
- runtime->silence_start = ofs - runtime->silence_filled;
+ runtime->silence_start = ofs;
}
- if ((snd_pcm_sframes_t)runtime->silence_start < 0)
- runtime->silence_start += runtime->boundary;
}
frames = runtime->buffer_size - runtime->silence_filled;
}
- snd_assert(frames <= runtime->buffer_size, return);
+ if (snd_BUG_ON(frames > runtime->buffer_size))
+ return;
if (frames == 0)
return;
- ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
+ ofs = runtime->silence_start % runtime->buffer_size;
while (frames > 0) {
transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
@@ -99,7 +102,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
if (substream->ops->silence) {
int err;
err = substream->ops->silence(substream, -1, ofs, transfer);
- snd_assert(err >= 0, );
+ snd_BUG_ON(err < 0);
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
@@ -111,7 +114,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
for (c = 0; c < channels; ++c) {
int err;
err = substream->ops->silence(substream, c, ofs, transfer);
- snd_assert(err >= 0, );
+ snd_BUG_ON(err < 0);
}
} else {
size_t dma_csize = runtime->dma_bytes / channels;
@@ -127,42 +130,152 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
}
}
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
+ char *name, size_t len)
+{
+ snprintf(name, len, "pcmC%dD%d%c:%d",
+ substream->pcm->card->number,
+ substream->pcm->device,
+ substream->stream ? 'c' : 'p',
+ substream->number);
+}
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
+
+#define XRUN_DEBUG_BASIC (1<<0)
+#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
+#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
+#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
+#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
+#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
+#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
+
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+
+#define xrun_debug(substream, mask) \
+ ((substream)->pstr->xrun_debug & (mask))
+#else
+#define xrun_debug(substream, mask) 0
+#endif
+
+#define dump_stack_on_xrun(substream) do { \
+ if (xrun_debug(substream, XRUN_DEBUG_STACK)) \
+ dump_stack(); \
+ } while (0)
+
static void xrun(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_warn(substream->pcm, "XRUN: %s\n", name);
+ dump_stack_on_xrun(substream);
+ }
+}
+
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (substream->pstr->xrun_debug) {
- snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
- substream->pcm->card->number,
- substream->pcm->device,
- substream->stream ? 'c' : 'p');
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
+#define hw_ptr_error(substream, fmt, args...) \
+ do { \
+ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
+ xrun_log_show(substream); \
+ pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \
+ dump_stack_on_xrun(substream); \
+ } \
+ } while (0)
+
+#define XRUN_LOG_CNT 10
+
+struct hwptr_log_entry {
+ unsigned int in_interrupt;
+ unsigned long jiffies;
+ snd_pcm_uframes_t pos;
+ snd_pcm_uframes_t period_size;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t old_hw_ptr;
+ snd_pcm_uframes_t hw_ptr_base;
+};
+
+struct snd_pcm_hwptr_log {
+ unsigned int idx;
+ unsigned int hit: 1;
+ struct hwptr_log_entry entries[XRUN_LOG_CNT];
+};
+
+static void xrun_log(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t pos, int in_interrupt)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
+ struct hwptr_log_entry *entry;
+
+ if (log == NULL) {
+ log = kzalloc(sizeof(*log), GFP_ATOMIC);
+ if (log == NULL)
+ return;
+ runtime->hwptr_log = log;
+ } else {
+ if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
+ return;
}
-#endif
+ entry = &log->entries[log->idx];
+ entry->in_interrupt = in_interrupt;
+ entry->jiffies = jiffies;
+ entry->pos = pos;
+ entry->period_size = runtime->period_size;
+ entry->buffer_size = runtime->buffer_size;
+ entry->old_hw_ptr = runtime->status->hw_ptr;
+ entry->hw_ptr_base = runtime->hw_ptr_base;
+ log->idx = (log->idx + 1) % XRUN_LOG_CNT;
}
-static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void xrun_log_show(struct snd_pcm_substream *substream)
{
- snd_pcm_uframes_t pos;
+ struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
+ struct hwptr_log_entry *entry;
+ char name[16];
+ unsigned int idx;
+ int cnt;
- pos = substream->ops->pointer(substream);
- if (pos == SNDRV_PCM_POS_XRUN)
- return pos; /* XRUN */
- if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
- getnstimeofday((struct timespec *)&runtime->status->tstamp);
-#ifdef CONFIG_SND_DEBUG
- if (pos >= runtime->buffer_size) {
- snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+ if (log == NULL)
+ return;
+ if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
+ return;
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
+ entry = &log->entries[idx];
+ if (entry->period_size == 0)
+ break;
+ pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
+ "hwptr=%ld/%ld\n",
+ name, entry->in_interrupt ? "[Q] " : "",
+ entry->jiffies,
+ (unsigned long)entry->pos,
+ (unsigned long)entry->period_size,
+ (unsigned long)entry->buffer_size,
+ (unsigned long)entry->old_hw_ptr,
+ (unsigned long)entry->hw_ptr_base);
+ idx++;
+ idx %= XRUN_LOG_CNT;
}
-#endif
- pos -= pos % runtime->min_align;
- return pos;
+ log->hit = 1;
}
-static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
+
+#define hw_ptr_error(substream, fmt, args...) do { } while (0)
+#define xrun_log(substream, pos, in_interrupt) do { } while (0)
+#define xrun_log_show(substream) do { } while (0)
+
+#endif
+
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime)
{
snd_pcm_uframes_t avail;
@@ -172,104 +285,277 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
avail = snd_pcm_capture_avail(runtime);
if (avail > runtime->avail_max)
runtime->avail_max = avail;
- if (avail >= runtime->stop_threshold) {
- if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (avail >= runtime->buffer_size) {
snd_pcm_drain_done(substream);
- else
+ return -EPIPE;
+ }
+ } else {
+ if (avail >= runtime->stop_threshold) {
xrun(substream);
- return -EPIPE;
+ return -EPIPE;
+ }
}
- if (avail >= runtime->control->avail_min)
+ if (runtime->twake) {
+ if (avail >= runtime->twake)
+ wake_up(&runtime->tsleep);
+ } else if (avail >= runtime->control->avail_min)
wake_up(&runtime->sleep);
return 0;
}
-static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
+static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
+ unsigned int in_interrupt)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
- snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt;
- snd_pcm_sframes_t delta;
+ snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
+ snd_pcm_sframes_t hdelta, delta;
+ unsigned long jdelta;
+ unsigned long curr_jiffies;
+ struct timespec curr_tstamp;
+ struct timespec audio_tstamp;
+ int crossed_boundary = 0;
+
+ old_hw_ptr = runtime->status->hw_ptr;
+
+ /*
+ * group pointer, time and jiffies reads to allow for more
+ * accurate correlations/corrections.
+ * The values are stored at the end of this routine after
+ * corrections for hw_ptr position
+ */
+ pos = substream->ops->pointer(substream);
+ curr_jiffies = jiffies;
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
+
+ if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
+ (substream->ops->wall_clock))
+ substream->ops->wall_clock(substream, &audio_tstamp);
+ }
- pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream);
return -EPIPE;
}
- if (runtime->period_size == runtime->buffer_size)
- goto __next_buf;
- new_hw_ptr = runtime->hw_ptr_base + pos;
- hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
-
- delta = hw_ptr_interrupt - new_hw_ptr;
- if (delta > 0) {
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->periods > 1 && substream->pstr->xrun_debug) {
- snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
+ if (pos >= runtime->buffer_size) {
+ if (printk_ratelimit()) {
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ xrun_log_show(substream);
+ pcm_err(substream->pcm,
+ "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ name, pos, runtime->buffer_size,
+ runtime->period_size);
+ }
+ pos = 0;
+ }
+ pos -= pos % runtime->min_align;
+ if (xrun_debug(substream, XRUN_DEBUG_LOG))
+ xrun_log(substream, pos, in_interrupt);
+ hw_base = runtime->hw_ptr_base;
+ new_hw_ptr = hw_base + pos;
+ if (in_interrupt) {
+ /* we know that one period was processed */
+ /* delta = "expected next hw_ptr" for in_interrupt != 0 */
+ delta = runtime->hw_ptr_interrupt + runtime->period_size;
+ if (delta > new_hw_ptr) {
+ /* check for double acknowledged interrupts */
+ hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary) {
+ hw_base = 0;
+ crossed_boundary++;
+ }
+ new_hw_ptr = hw_base + pos;
+ goto __delta;
}
-#endif
- return 0;
}
- __next_buf:
- runtime->hw_ptr_base += runtime->buffer_size;
- if (runtime->hw_ptr_base == runtime->boundary)
- runtime->hw_ptr_base = 0;
- new_hw_ptr = runtime->hw_ptr_base + pos;
+ }
+ /* new_hw_ptr might be lower than old_hw_ptr in case when */
+ /* pointer crosses the end of the ring buffer */
+ if (new_hw_ptr < old_hw_ptr) {
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary) {
+ hw_base = 0;
+ crossed_boundary++;
+ }
+ new_hw_ptr = hw_base + pos;
+ }
+ __delta:
+ delta = new_hw_ptr - old_hw_ptr;
+ if (delta < 0)
+ delta += runtime->boundary;
+ if (xrun_debug(substream, in_interrupt ?
+ XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_dbg(substream->pcm,
+ "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n",
+ in_interrupt ? "period" : "hwptr",
+ name,
+ (unsigned int)pos,
+ (unsigned int)runtime->period_size,
+ (unsigned int)runtime->buffer_size,
+ (unsigned long)delta,
+ (unsigned long)old_hw_ptr,
+ (unsigned long)new_hw_ptr,
+ (unsigned long)runtime->hw_ptr_base);
+ }
+
+ if (runtime->no_period_wakeup) {
+ snd_pcm_sframes_t xrun_threshold;
+ /*
+ * Without regular period interrupts, we have to check
+ * the elapsed time to detect xruns.
+ */
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+ goto no_delta_check;
+ hdelta = jdelta - delta * HZ / runtime->rate;
+ xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
+ while (hdelta > xrun_threshold) {
+ delta += runtime->buffer_size;
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary) {
+ hw_base = 0;
+ crossed_boundary++;
+ }
+ new_hw_ptr = hw_base + pos;
+ hdelta -= runtime->hw_ptr_buffer_jiffies;
+ }
+ goto no_delta_check;
+ }
+
+ /* something must be really wrong */
+ if (delta >= runtime->buffer_size + runtime->period_size) {
+ hw_ptr_error(substream,
+ "Unexpected hw_pointer value %s"
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
+ "old_hw_ptr=%ld)\n",
+ in_interrupt ? "[Q] " : "[P]",
+ substream->stream, (long)pos,
+ (long)new_hw_ptr, (long)old_hw_ptr);
+ return 0;
}
+ /* Do jiffies check only in xrun_debug mode */
+ if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
+ goto no_jiffies_check;
+
+ /* Skip the jiffies check for hardwares with BATCH flag.
+ * Such hardware usually just increases the position at each IRQ,
+ * thus it can't give any strange position.
+ */
+ if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
+ goto no_jiffies_check;
+ hdelta = delta;
+ if (hdelta < runtime->delay)
+ goto no_jiffies_check;
+ hdelta -= runtime->delay;
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
+ delta = jdelta /
+ (((runtime->period_size * HZ) / runtime->rate)
+ + HZ/100);
+ /* move new_hw_ptr according jiffies not pos variable */
+ new_hw_ptr = old_hw_ptr;
+ hw_base = delta;
+ /* use loop to avoid checks for delta overflows */
+ /* the delta value is small or zero in most cases */
+ while (delta > 0) {
+ new_hw_ptr += runtime->period_size;
+ if (new_hw_ptr >= runtime->boundary) {
+ new_hw_ptr -= runtime->boundary;
+ crossed_boundary--;
+ }
+ delta--;
+ }
+ /* align hw_base to buffer_size */
+ hw_ptr_error(substream,
+ "hw_ptr skipping! %s"
+ "(pos=%ld, delta=%ld, period=%ld, "
+ "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
+ in_interrupt ? "[Q] " : "",
+ (long)pos, (long)hdelta,
+ (long)runtime->period_size, jdelta,
+ ((hdelta * HZ) / runtime->rate), hw_base,
+ (unsigned long)old_hw_ptr,
+ (unsigned long)new_hw_ptr);
+ /* reset values to proper state */
+ delta = 0;
+ hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
+ }
+ no_jiffies_check:
+ if (delta > runtime->period_size + runtime->period_size / 2) {
+ hw_ptr_error(substream,
+ "Lost interrupts? %s"
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
+ "old_hw_ptr=%ld)\n",
+ in_interrupt ? "[Q] " : "",
+ substream->stream, (long)delta,
+ (long)new_hw_ptr,
+ (long)old_hw_ptr);
+ }
+
+ no_delta_check:
+ if (runtime->status->hw_ptr == new_hw_ptr)
+ return 0;
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr);
+ if (in_interrupt) {
+ delta = new_hw_ptr - runtime->hw_ptr_interrupt;
+ if (delta < 0)
+ delta += runtime->boundary;
+ delta -= (snd_pcm_uframes_t)delta % runtime->period_size;
+ runtime->hw_ptr_interrupt += delta;
+ if (runtime->hw_ptr_interrupt >= runtime->boundary)
+ runtime->hw_ptr_interrupt -= runtime->boundary;
+ }
+ runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
- runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size;
+ runtime->hw_ptr_jiffies = curr_jiffies;
+ if (crossed_boundary) {
+ snd_BUG_ON(crossed_boundary != 1);
+ runtime->hw_ptr_wrap += runtime->boundary;
+ }
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ runtime->status->tstamp = curr_tstamp;
+
+ if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
+ /*
+ * no wall clock available, provide audio timestamp
+ * derived from pointer position+delay
+ */
+ u64 audio_frames, audio_nsecs;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ - runtime->delay;
+ else
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ + runtime->delay;
+ audio_nsecs = div_u64(audio_frames * 1000000000LL,
+ runtime->rate);
+ audio_tstamp = ns_to_timespec(audio_nsecs);
+ }
+ runtime->status->audio_tstamp = audio_tstamp;
+ }
- return snd_pcm_update_hw_ptr_post(substream, runtime);
+ return snd_pcm_update_state(substream, runtime);
}
/* CAUTION: call it with irq disabled */
int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t pos;
- snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
- snd_pcm_sframes_t delta;
-
- old_hw_ptr = runtime->status->hw_ptr;
- pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
- if (pos == SNDRV_PCM_POS_XRUN) {
- xrun(substream);
- return -EPIPE;
- }
- new_hw_ptr = runtime->hw_ptr_base + pos;
-
- delta = old_hw_ptr - new_hw_ptr;
- if (delta > 0) {
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->periods > 2 && substream->pstr->xrun_debug) {
- snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
- }
-#endif
- return 0;
- }
- runtime->hw_ptr_base += runtime->buffer_size;
- if (runtime->hw_ptr_base == runtime->boundary)
- runtime->hw_ptr_base = 0;
- new_hw_ptr = runtime->hw_ptr_base + pos;
- }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- runtime->silence_size > 0)
- snd_pcm_playback_silence(substream, new_hw_ptr);
-
- runtime->status->hw_ptr = new_hw_ptr;
-
- return snd_pcm_update_hw_ptr_post(substream, runtime);
+ return snd_pcm_update_hw_ptr0(substream, 0);
}
/**
@@ -280,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;
@@ -357,11 +644,11 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
{
u_int64_t n = (u_int64_t) a * b;
if (c == 0) {
- snd_assert(n > 0, );
+ snd_BUG_ON(!n);
*r = 0;
return UINT_MAX;
}
- div64_32(&n, c, r);
+ n = div_u64_rem(n, c, r);
if (n >= UINT_MAX) {
*r = 0;
return UINT_MAX;
@@ -378,12 +665,14 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
* The interval is changed to the range satisfying both intervals.
* The interval status (min, max, integer, etc.) are evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
int changed = 0;
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (i->min < v->min) {
i->min = v->min;
i->openmin = v->openmin;
@@ -426,7 +715,8 @@ EXPORT_SYMBOL(snd_interval_refine);
static int snd_interval_refine_first(struct snd_interval *i)
{
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (snd_interval_single(i))
return 0;
i->max = i->min;
@@ -438,7 +728,8 @@ static int snd_interval_refine_first(struct snd_interval *i)
static int snd_interval_refine_last(struct snd_interval *i)
{
- snd_assert(!snd_interval_empty(i), return -EINVAL);
+ if (snd_BUG_ON(snd_interval_empty(i)))
+ return -EINVAL;
if (snd_interval_single(i))
return 0;
i->min = i->max;
@@ -574,16 +865,20 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
unsigned int rats_count, struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp)
{
- unsigned int best_num, best_diff, best_den;
+ unsigned int best_num, best_den;
+ int best_diff;
unsigned int k;
struct snd_interval t;
int err;
+ unsigned int result_num, result_den;
+ int result_diff;
best_num = best_den = best_diff = 0;
for (k = 0; k < rats_count; ++k) {
@@ -593,7 +888,7 @@ int snd_interval_ratnum(struct snd_interval *i,
int diff;
if (q == 0)
q = 1;
- den = div_down(num, q);
+ den = div_up(num, q);
if (den < rats[k].den_min)
continue;
if (den > rats[k].den_max)
@@ -605,6 +900,8 @@ int snd_interval_ratnum(struct snd_interval *i,
den -= r;
}
diff = num - q * den;
+ if (diff < 0)
+ diff = -diff;
if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
@@ -619,6 +916,9 @@ int snd_interval_ratnum(struct snd_interval *i,
t.min = div_down(best_num, best_den);
t.openmin = !!(best_num % best_den);
+ result_num = best_num;
+ result_diff = best_diff;
+ result_den = best_den;
best_num = best_den = best_diff = 0;
for (k = 0; k < rats_count; ++k) {
unsigned int num = rats[k].num;
@@ -629,7 +929,7 @@ int snd_interval_ratnum(struct snd_interval *i,
i->empty = 1;
return -EINVAL;
}
- den = div_up(num, q);
+ den = div_down(num, q);
if (den > rats[k].den_max)
continue;
if (den < rats[k].den_min)
@@ -641,6 +941,8 @@ int snd_interval_ratnum(struct snd_interval *i,
den += rats[k].den_step - r;
}
diff = q * den - num;
+ if (diff < 0)
+ diff = -diff;
if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
@@ -660,10 +962,14 @@ int snd_interval_ratnum(struct snd_interval *i,
return err;
if (snd_interval_single(i)) {
+ if (best_diff * result_den < result_diff * best_den) {
+ result_num = best_num;
+ result_den = best_den;
+ }
if (nump)
- *nump = best_num;
+ *nump = result_num;
if (denp)
- *denp = best_den;
+ *denp = result_den;
}
return err;
}
@@ -678,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,
@@ -777,47 +1084,31 @@ static int snd_interval_ratden(struct snd_interval *i,
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
-int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
+int snd_interval_list(struct snd_interval *i, unsigned int count,
+ const unsigned int *list, unsigned int mask)
{
unsigned int k;
- int changed = 0;
+ struct snd_interval list_range;
+
+ if (!count) {
+ i->empty = 1;
+ return -EINVAL;
+ }
+ snd_interval_any(&list_range);
+ list_range.min = UINT_MAX;
+ list_range.max = 0;
for (k = 0; k < count; k++) {
if (mask && !(mask & (1 << k)))
continue;
- if (i->min == list[k] && !i->openmin)
- goto _l1;
- if (i->min < list[k]) {
- i->min = list[k];
- i->openmin = 0;
- changed = 1;
- goto _l1;
- }
- }
- i->empty = 1;
- return -EINVAL;
- _l1:
- for (k = count; k-- > 0;) {
- if (mask && !(mask & (1 << k)))
+ if (!snd_interval_test(i, list[k]))
continue;
- if (i->max == list[k] && !i->openmax)
- goto _l2;
- if (i->max > list[k]) {
- i->max = list[k];
- i->openmax = 0;
- changed = 1;
- goto _l2;
- }
+ list_range.min = min(list_range.min, list[k]);
+ list_range.max = max(list_range.max, list[k]);
}
- i->empty = 1;
- return -EINVAL;
- _l2:
- if (snd_interval_checkempty(i)) {
- i->empty = 1;
- return -EINVAL;
- }
- return changed;
+ return snd_interval_refine(i, &list_range);
}
EXPORT_SYMBOL(snd_interval_list);
@@ -854,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,
@@ -870,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));
@@ -887,7 +1180,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
c->private = private;
k = 0;
while (1) {
- snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL);
+ if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
+ va_end(args);
+ return -EINVAL;
+ }
c->deps[k++] = dep;
if (dep < 0)
break;
@@ -896,17 +1192,19 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
constrs->rules_num++;
va_end(args);
return 0;
-}
+}
EXPORT_SYMBOL(snd_pcm_hw_rule_add);
/**
- * snd_pcm_hw_constraint_mask
+ * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
* @runtime: PCM runtime instance
* @var: hw_params variable to apply the mask
* @mask: the bitmap mask
*
- * Apply the constraint of the given bitmap mask to a mask parameter.
+ * Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
@@ -921,12 +1219,14 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param
}
/**
- * snd_pcm_hw_constraint_mask64
+ * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
* @runtime: PCM runtime instance
* @var: hw_params variable to apply the mask
* @mask: the 64bit bitmap mask
*
- * Apply the constraint of the given bitmap mask to a mask parameter.
+ * Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
@@ -940,13 +1240,17 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
return -EINVAL;
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
/**
- * snd_pcm_hw_constraint_integer
+ * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
* @runtime: PCM runtime instance
* @var: hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{
@@ -957,13 +1261,16 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa
EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
/**
- * snd_pcm_hw_constraint_minmax
+ * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval
* @runtime: PCM runtime instance
* @var: hw_params variable to apply the range
* @min: the minimal value
* @max: the maximal value
*
* Apply the min/max range constraint to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
unsigned int min, unsigned int max)
@@ -988,21 +1295,23 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
/**
- * snd_pcm_hw_constraint_list
+ * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the list constraint
* @l: list
*
* Apply the list of constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_list *l)
+ const struct snd_pcm_hw_constraint_list *l)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_list, l,
+ snd_pcm_hw_rule_list, (void *)l,
var, -1);
}
@@ -1024,11 +1333,13 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
}
/**
- * snd_pcm_hw_constraint_ratnums
+ * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the ratnums constraint
* @r: struct snd_ratnums constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1057,11 +1368,13 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
}
/**
- * snd_pcm_hw_constraint_ratdens
+ * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the ratdens constraint
* @r: struct snd_ratdens constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1088,11 +1401,13 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
}
/**
- * snd_pcm_hw_constraint_msbits
+ * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
* @runtime: PCM runtime instance
* @cond: condition bits
* @width: sample bits width
* @msbits: msbits width
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1116,11 +1431,13 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
}
/**
- * snd_pcm_hw_constraint_step
+ * snd_pcm_hw_constraint_step - add a hw constraint step rule
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the step constraint
* @step: step size
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1136,7 +1453,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
- static int pow2_sizes[] = {
+ static unsigned int pow2_sizes[] = {
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
@@ -1147,10 +1464,12 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm
}
/**
- * snd_pcm_hw_constraint_pow2
+ * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the power-of-2 constraint
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1163,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)
{
@@ -1195,13 +1542,13 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
EXPORT_SYMBOL(_snd_pcm_hw_params_any);
/**
- * snd_pcm_hw_param_value
+ * snd_pcm_hw_param_value - return @params field @var value
* @params: the hw_params instance
* @var: parameter to retrieve
- * @dir: pointer to the direction (-1,0,1) or NULL
+ * @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Return the value for field PAR if it's fixed in configuration space
- * defined by PARAMS. Return -EINVAL otherwise
+ * Return: The value for field @var if it's fixed in configuration space
+ * defined by @params. -%EINVAL otherwise.
*/
int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
@@ -1264,15 +1611,16 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
/**
- * snd_pcm_hw_param_first
+ * snd_pcm_hw_param_first - refine config space and return minimum value
* @pcm: PCM instance
* @params: the hw_params instance
* @var: parameter to retrieve
- * @dir: pointer to the direction (-1,0,1) or NULL
+ * @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Inside configuration space defined by PARAMS remove from PAR all
+ * Inside configuration space defined by @params remove from @var all
* values > minimum. Reduce configuration space accordingly.
- * Return the minimum.
+ *
+ * Return: The minimum, or a negative error code on failure.
*/
int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1283,7 +1631,8 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
@@ -1309,15 +1658,16 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
/**
- * snd_pcm_hw_param_last
+ * snd_pcm_hw_param_last - refine config space and return maximum value
* @pcm: PCM instance
* @params: the hw_params instance
* @var: parameter to retrieve
- * @dir: pointer to the direction (-1,0,1) or NULL
+ * @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Inside configuration space defined by PARAMS remove from PAR all
+ * Inside configuration space defined by @params remove from @var all
* values < maximum. Reduce configuration space accordingly.
- * Return the maximum.
+ *
+ * Return: The maximum, or a negative error code on failure.
*/
int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1328,7 +1678,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
@@ -1336,14 +1687,16 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
EXPORT_SYMBOL(snd_pcm_hw_param_last);
/**
- * snd_pcm_hw_param_choose
+ * snd_pcm_hw_param_choose - choose a configuration defined by @params
* @pcm: PCM instance
* @params: the hw_params instance
*
- * Choose one configuration from configuration space defined by PARAMS
+ * Choose one configuration from configuration space defined by @params.
* The configuration chosen is that obtained fixing in this order:
* first access, first format, first subformat, min channels,
* min rate, min period time, max buffer size, min tick time
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params)
@@ -1366,7 +1719,8 @@ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
else
err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
- snd_assert(err >= 0, return err);
+ if (snd_BUG_ON(err < 0))
+ return err;
}
return 0;
}
@@ -1380,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;
}
@@ -1421,6 +1777,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
return 0;
}
+static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
+ void *arg)
+{
+ struct snd_pcm_hw_params *params = arg;
+ snd_pcm_format_t format;
+ int channels, width;
+
+ params->fifo_size = substream->runtime->hw.fifo_size;
+ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
+ format = params_format(params);
+ channels = params_channels(params);
+ width = snd_pcm_format_physical_width(format);
+ params->fifo_size /= width * channels;
+ }
+ return 0;
+}
+
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance
@@ -1430,7 +1803,7 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
@@ -1442,114 +1815,21 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
return snd_pcm_lib_ioctl_channel_info(substream, arg);
+ case SNDRV_PCM_IOCTL1_FIFO_SIZE:
+ return snd_pcm_lib_ioctl_fifo_size(substream, arg);
}
return -ENXIO;
}
EXPORT_SYMBOL(snd_pcm_lib_ioctl);
-/*
- * Conditions
- */
-
-static void snd_pcm_system_tick_set(struct snd_pcm_substream *substream,
- unsigned long ticks)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- if (ticks == 0)
- del_timer(&runtime->tick_timer);
- else {
- ticks += (1000000 / HZ) - 1;
- ticks /= (1000000 / HZ);
- mod_timer(&runtime->tick_timer, jiffies + ticks);
- }
-}
-
-/* Temporary alias */
-void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks)
-{
- snd_pcm_system_tick_set(substream, ticks);
-}
-
-void snd_pcm_tick_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t frames = ULONG_MAX;
- snd_pcm_uframes_t avail, dist;
- unsigned int ticks;
- u_int64_t n;
- u_int32_t r;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (runtime->silence_size >= runtime->boundary) {
- frames = 1;
- } else if (runtime->silence_size > 0 &&
- runtime->silence_filled < runtime->buffer_size) {
- snd_pcm_sframes_t noise_dist;
- noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
- if (noise_dist > (snd_pcm_sframes_t)runtime->silence_threshold)
- frames = noise_dist - runtime->silence_threshold;
- }
- avail = snd_pcm_playback_avail(runtime);
- } else {
- avail = snd_pcm_capture_avail(runtime);
- }
- if (avail < runtime->control->avail_min) {
- snd_pcm_sframes_t n = runtime->control->avail_min - avail;
- if (n > 0 && frames > (snd_pcm_uframes_t)n)
- frames = n;
- }
- if (avail < runtime->buffer_size) {
- snd_pcm_sframes_t n = runtime->buffer_size - avail;
- if (n > 0 && frames > (snd_pcm_uframes_t)n)
- frames = n;
- }
- if (frames == ULONG_MAX) {
- snd_pcm_tick_set(substream, 0);
- return;
- }
- dist = runtime->status->hw_ptr - runtime->hw_ptr_base;
- /* Distance to next interrupt */
- dist = runtime->period_size - dist % runtime->period_size;
- if (dist <= frames) {
- snd_pcm_tick_set(substream, 0);
- return;
- }
- /* the base time is us */
- n = frames;
- n *= 1000000;
- div64_32(&n, runtime->tick_time * runtime->rate, &r);
- ticks = n + (r > 0 ? 1 : 0);
- if (ticks < runtime->sleep_min)
- ticks = runtime->sleep_min;
- snd_pcm_tick_set(substream, (unsigned long) ticks);
-}
-
-void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime;
- unsigned long flags;
-
- snd_assert(substream != NULL, return);
- runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
-
- snd_pcm_stream_lock_irqsave(substream, flags);
- if (!snd_pcm_running(substream) ||
- snd_pcm_update_hw_ptr(substream) < 0)
- goto _end;
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
- _end:
- snd_pcm_stream_unlock_irqrestore(substream, flags);
-}
-
/**
* snd_pcm_period_elapsed - update the pcm status for the next period
* @substream: the pcm substream instance
*
* This function is called from the interrupt handler when the
* PCM has processed the period size. It will update the current
- * pointer, set up the tick, wake up sleepers, etc.
+ * pointer, wake up sleepers, etc.
*
* Even if more than one periods have elapsed since the last call, you
* have to call this only once.
@@ -1559,22 +1839,20 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
unsigned long flags;
- snd_assert(substream != NULL, return);
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
if (runtime->transfer_ack_begin)
runtime->transfer_ack_begin(substream);
snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
- snd_pcm_update_hw_ptr_interrupt(substream) < 0)
+ snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
_end:
snd_pcm_stream_unlock_irqrestore(substream, flags);
if (runtime->transfer_ack_end)
@@ -1584,6 +1862,98 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
EXPORT_SYMBOL(snd_pcm_period_elapsed);
+/*
+ * Wait until avail_min data becomes available
+ * Returns a negative error code if any error occurs during operation.
+ * The available space is stored on availp. When err = 0 and avail = 0
+ * on the capture stream, it indicates the stream is in DRAINING state.
+ */
+static int wait_for_avail(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t *availp)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ wait_queue_t wait;
+ int err = 0;
+ snd_pcm_uframes_t avail = 0;
+ 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;
+ }
+
+ /*
+ * 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(wait_time);
+
+ snd_pcm_stream_lock_irq(substream);
+ set_current_state(TASK_INTERRUPTIBLE);
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_SUSPENDED:
+ err = -ESTRPIPE;
+ goto _endloop;
+ case SNDRV_PCM_STATE_XRUN:
+ err = -EPIPE;
+ goto _endloop;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (is_playback)
+ err = -EPIPE;
+ else
+ avail = 0; /* indicate draining */
+ goto _endloop;
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ err = -EBADFD;
+ goto _endloop;
+ case SNDRV_PCM_STATE_PAUSED:
+ continue;
+ }
+ if (!tout) {
+ pcm_dbg(substream->pcm,
+ "%s write error (DMA or IRQ trouble?)\n",
+ is_playback ? "playback" : "capture");
+ err = -EIO;
+ break;
+ }
+ }
+ _endloop:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&runtime->tsleep, &wait);
+ *availp = avail;
+ return err;
+}
+
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
@@ -1597,7 +1967,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- snd_assert(runtime->dma_area, return -EFAULT);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
@@ -1617,12 +1986,11 @@ 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)
return 0;
- if (size > runtime->xfer_align)
- size -= size % runtime->xfer_align;
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
@@ -1641,99 +2009,40 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
goto _end_unlock;
}
+ runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+ avail = snd_pcm_playback_avail(runtime);
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_playback_avail(runtime);
- if (((avail < runtime->control->avail_min && size > avail) ||
- (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
- wait_queue_t wait;
- enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
- long tout;
-
+ if (!avail) {
if (nonblock) {
err = -EAGAIN;
goto _end_unlock;
}
-
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&runtime->sleep, &wait);
- while (1) {
- if (signal_pending(current)) {
- state = SIGNALED;
- break;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- snd_pcm_stream_unlock_irq(substream);
- tout = schedule_timeout(10 * HZ);
- snd_pcm_stream_lock_irq(substream);
- if (tout == 0) {
- if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
- runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
- state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
- break;
- }
- }
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- case SNDRV_PCM_STATE_DRAINING:
- state = ERROR;
- goto _end_loop;
- case SNDRV_PCM_STATE_SUSPENDED:
- state = SUSPENDED;
- goto _end_loop;
- case SNDRV_PCM_STATE_SETUP:
- state = DROPPED;
- goto _end_loop;
- default:
- break;
- }
- avail = snd_pcm_playback_avail(runtime);
- if (avail >= runtime->control->avail_min) {
- state = READY;
- break;
- }
- }
- _end_loop:
- remove_wait_queue(&runtime->sleep, &wait);
-
- switch (state) {
- case ERROR:
- err = -EPIPE;
- goto _end_unlock;
- case SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- case SIGNALED:
- err = -ERESTARTSYS;
- goto _end_unlock;
- case EXPIRED:
- snd_printd("playback write error (DMA or IRQ trouble?)\n");
- err = -EIO;
- goto _end_unlock;
- case DROPPED:
- err = -EBADFD;
+ runtime->twake = min_t(snd_pcm_uframes_t, size,
+ runtime->control->avail_min ? : 1);
+ err = wait_for_avail(substream, &avail);
+ if (err < 0)
goto _end_unlock;
- default:
- break;
- }
}
- if (avail > runtime->xfer_align)
- avail -= avail % runtime->xfer_align;
frames = size > avail ? avail : size;
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
if (frames > cont)
frames = cont;
- snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+ if (snd_BUG_ON(!frames)) {
+ runtime->twake = 0;
+ snd_pcm_stream_unlock_irq(substream);
+ return -EINVAL;
+ }
appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
- if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
- goto _end;
+ err = transfer(substream, appl_ofs, data, offset, frames);
snd_pcm_stream_lock_irq(substream);
+ if (err < 0)
+ goto _end_unlock;
switch (runtime->status->state) {
case SNDRV_PCM_STATE_XRUN:
err = -EPIPE;
@@ -1754,34 +2063,46 @@ 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);
if (err < 0)
goto _end_unlock;
}
- if (runtime->sleep_min &&
- runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_tick_prepare(substream);
}
_end_unlock:
+ runtime->twake = 0;
+ if (xfer > 0 && err >= 0)
+ snd_pcm_update_state(substream, runtime);
snd_pcm_stream_unlock_irq(substream);
- _end:
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
-snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
+/* sanity-check for read/write methods */
+static int pcm_sanity_check(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- int nonblock;
-
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
+ return -EINVAL;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
+{
+ struct snd_pcm_runtime *runtime;
+ int nonblock;
+ int err;
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
+ runtime = substream->runtime;
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
@@ -1804,7 +2125,8 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
int channels = runtime->channels;
int c;
if (substream->ops->copy) {
- snd_assert(substream->ops->silence != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream->ops->silence))
+ return -EINVAL;
for (c = 0; c < channels; ++c, ++bufs) {
if (*bufs == NULL) {
if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
@@ -1818,7 +2140,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
} else {
/* default transfer behaviour */
size_t dma_csize = runtime->dma_bytes / channels;
- snd_assert(runtime->dma_area, return -EFAULT);
for (c = 0; c < channels; ++c, ++bufs) {
char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
if (*bufs == NULL) {
@@ -1839,14 +2160,12 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
@@ -1870,7 +2189,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- snd_assert(runtime->dma_area, return -EFAULT);
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
@@ -1886,12 +2204,11 @@ 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)
return 0;
- if (size > runtime->xfer_align)
- size -= size % runtime->xfer_align;
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
@@ -1917,106 +2234,47 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
goto _end_unlock;
}
+ runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+ avail = snd_pcm_capture_avail(runtime);
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- __draining:
- avail = snd_pcm_capture_avail(runtime);
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
- if (avail < runtime->xfer_align) {
- err = -EPIPE;
+ if (!avail) {
+ if (runtime->status->state ==
+ SNDRV_PCM_STATE_DRAINING) {
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock;
}
- } else if ((avail < runtime->control->avail_min && size > avail) ||
- (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
- wait_queue_t wait;
- enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
- long tout;
-
if (nonblock) {
err = -EAGAIN;
goto _end_unlock;
}
-
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&runtime->sleep, &wait);
- while (1) {
- if (signal_pending(current)) {
- state = SIGNALED;
- break;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- snd_pcm_stream_unlock_irq(substream);
- tout = schedule_timeout(10 * HZ);
- snd_pcm_stream_lock_irq(substream);
- if (tout == 0) {
- if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
- runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
- state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
- break;
- }
- }
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- state = ERROR;
- goto _end_loop;
- case SNDRV_PCM_STATE_SUSPENDED:
- state = SUSPENDED;
- goto _end_loop;
- case SNDRV_PCM_STATE_DRAINING:
- goto __draining;
- case SNDRV_PCM_STATE_SETUP:
- state = DROPPED;
- goto _end_loop;
- default:
- break;
- }
- avail = snd_pcm_capture_avail(runtime);
- if (avail >= runtime->control->avail_min) {
- state = READY;
- break;
- }
- }
- _end_loop:
- remove_wait_queue(&runtime->sleep, &wait);
-
- switch (state) {
- case ERROR:
- err = -EPIPE;
- goto _end_unlock;
- case SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- case SIGNALED:
- err = -ERESTARTSYS;
- goto _end_unlock;
- case EXPIRED:
- snd_printd("capture read error (DMA or IRQ trouble?)\n");
- err = -EIO;
- goto _end_unlock;
- case DROPPED:
- err = -EBADFD;
+ runtime->twake = min_t(snd_pcm_uframes_t, size,
+ runtime->control->avail_min ? : 1);
+ err = wait_for_avail(substream, &avail);
+ if (err < 0)
goto _end_unlock;
- default:
- break;
- }
+ if (!avail)
+ continue; /* draining */
}
- if (avail > runtime->xfer_align)
- avail -= avail % runtime->xfer_align;
frames = size > avail ? avail : size;
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
if (frames > cont)
frames = cont;
- snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+ if (snd_BUG_ON(!frames)) {
+ runtime->twake = 0;
+ snd_pcm_stream_unlock_irq(substream);
+ return -EINVAL;
+ }
appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
- if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
- goto _end;
+ err = transfer(substream, appl_ofs, data, offset, frames);
snd_pcm_stream_lock_irq(substream);
+ if (err < 0)
+ goto _end_unlock;
switch (runtime->status->state) {
case SNDRV_PCM_STATE_XRUN:
err = -EPIPE;
@@ -2037,13 +2295,13 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
offset += frames;
size -= frames;
xfer += frames;
- if (runtime->sleep_min &&
- runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_tick_prepare(substream);
+ avail -= frames;
}
_end_unlock:
+ runtime->twake = 0;
+ if (xfer > 0 && err >= 0)
+ snd_pcm_update_state(substream, runtime);
snd_pcm_stream_unlock_irq(substream);
- _end:
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
@@ -2051,14 +2309,12 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
@@ -2088,7 +2344,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
}
} else {
snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
- snd_assert(runtime->dma_area, return -EFAULT);
for (c = 0; c < channels; ++c, ++bufs) {
char *hwbuf;
char __user *buf;
@@ -2110,11 +2365,12 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime;
int nonblock;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ err = pcm_sanity_check(substream);
+ if (err < 0)
+ return err;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
- snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2125,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 be030cb4d37..54debc07f5c 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,11 +19,13 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/time.h>
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
@@ -49,19 +51,9 @@ static const size_t snd_minimum_buffer = 16384;
static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
{
struct snd_dma_buffer *dmab = &substream->dma_buffer;
+ size_t orig_size = size;
int err;
- snd_assert(size > 0, return -EINVAL);
-
- /* already reserved? */
- if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
- if (dmab->bytes >= size)
- return 0; /* yes */
- /* no, free the reserved block */
- snd_dma_free_pages(dmab);
- dmab->bytes = 0;
- }
-
do {
if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
size, dmab)) < 0) {
@@ -72,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;
}
@@ -82,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;
}
@@ -95,12 +88,14 @@ 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)
{
snd_pcm_lib_preallocate_dma_free(substream);
#ifdef CONFIG_SND_VERBOSE_PROCFS
+ snd_info_free_entry(substream->proc_prealloc_max_entry);
+ substream->proc_prealloc_max_entry = NULL;
snd_info_free_entry(substream->proc_prealloc_entry);
substream->proc_prealloc_entry = NULL;
#endif
@@ -113,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)
{
@@ -142,6 +137,18 @@ static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,
}
/*
+ * read callback for prealloc_max proc file
+ *
+ * prints the maximum allowed size in kB.
+ */
+static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+ snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_max / 1024);
+}
+
+/*
* write callback for prealloc proc file
*
* accepts the preallocation size in kB.
@@ -203,6 +210,15 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
}
}
substream->proc_prealloc_entry = entry;
+ if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
+ entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
+ entry->private_data = substream;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ substream->proc_prealloc_max_entry = entry;
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -231,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,
@@ -256,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,
@@ -284,13 +295,15 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
+#ifdef CONFIG_SND_DMA_SGBUF
/**
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
* @substream: the pcm substream instance
* @offset: the buffer offset
*
- * Returns the page struct at the given buffer offset.
* Used as the page callback of PCM ops.
+ *
+ * Return: The page struct at the given buffer offset. %NULL on failure.
*/
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
{
@@ -303,6 +316,7 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
}
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
+#endif /* CONFIG_SND_DMA_SGBUF */
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
@@ -312,7 +326,7 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
* Allocates the DMA buffer on the BUS type given earlier to
* snd_pcm_lib_preallocate_xxx_pages().
*
- * Returns 1 if the buffer is changed, 0 if not changed, or a negative
+ * Return: 1 if the buffer is changed, 0 if not changed, or a negative
* code on failure.
*/
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
@@ -320,10 +334,12 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
struct snd_pcm_runtime *runtime;
struct snd_dma_buffer *dmab = NULL;
- snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL);
- snd_assert(substream != NULL, return -EINVAL);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
+ if (snd_BUG_ON(substream->dma_buffer.dev.type ==
+ SNDRV_DMA_TYPE_UNKNOWN))
+ return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EINVAL);
if (runtime->dma_buffer_p) {
/* perphaps, we might free the large DMA memory region
@@ -363,15 +379,15 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
*
* Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- snd_assert(substream != NULL, return -EINVAL);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EINVAL);
if (runtime->dma_area == NULL)
return 0;
if (runtime->dma_buffer_p != &substream->dma_buffer) {
@@ -384,3 +400,61 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(snd_pcm_lib_free_pages);
+
+int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
+ size_t size, gfp_t gfp_flags)
+{
+ struct snd_pcm_runtime *runtime;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
+ runtime = substream->runtime;
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes >= size)
+ return 0; /* already large enough */
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+ runtime->dma_bytes = size;
+ return 1;
+}
+EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
+
+/**
+ * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
+ * @substream: the substream with a buffer allocated by
+ * snd_pcm_lib_alloc_vmalloc_buffer()
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -EINVAL;
+ runtime = substream->runtime;
+ vfree(runtime->dma_area);
+ runtime->dma_area = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
+
+/**
+ * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
+ * @substream: the substream with a buffer allocated by
+ * snd_pcm_lib_alloc_vmalloc_buffer()
+ * @offset: offset in the buffer
+ *
+ * This function is to be used as the page callback in the PCM ops.
+ *
+ * Return: The page struct, or %NULL on failure.
+ */
+struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index 0019c59a779..4560ca0e565 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -1,6 +1,6 @@
/*
* PCM Interface - misc routines
- * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 1998 by Jaroslav Kysela <perex@perex.cz>
*
*
* This library is free software; you can redistribute it and/or modify
@@ -19,8 +19,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
#define SND_PCM_FORMAT_UNKNOWN (-1)
@@ -36,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 = {},
@@ -75,7 +78,7 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
},
[SNDRV_PCM_FORMAT_U24_BE] = {
.width = 24, .phys = 32, .le = 0, .signd = 0,
- .silence = { 0x80, 0x00, 0x00 },
+ .silence = { 0x00, 0x80, 0x00, 0x00 },
},
[SNDRV_PCM_FORMAT_S32_LE] = {
.width = 32, .phys = 32, .le = 1, .signd = 1,
@@ -129,6 +132,22 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 4, .phys = 4, .le = -1, .signd = -1,
.silence = {},
},
+ [SNDRV_PCM_FORMAT_G723_24] = {
+ .width = 3, .phys = 3, .le = -1, .signd = -1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_G723_40] = {
+ .width = 5, .phys = 5, .le = -1, .signd = -1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_DSD_U8] = {
+ .width = 8, .phys = 8, .le = 1, .signd = 0,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_LE] = {
+ .width = 16, .phys = 16, .le = 1, .signd = 0,
+ .silence = {},
+ },
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
@@ -187,6 +206,14 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 18, .phys = 24, .le = 0, .signd = 0,
.silence = { 0x02, 0x00, 0x00 },
},
+ [SNDRV_PCM_FORMAT_G723_24_1B] = {
+ .width = 3, .phys = 8, .le = -1, .signd = -1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_G723_40_1B] = {
+ .width = 5, .phys = 8, .le = -1, .signd = -1,
+ .silence = {},
+ },
};
@@ -194,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;
}
@@ -213,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)
@@ -232,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)
{
@@ -245,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;
}
@@ -264,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)
@@ -283,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;
}
@@ -302,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;
}
@@ -320,8 +347,9 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width);
/**
* snd_pcm_format_size - return the byte size of samples on the given format
* @format: the format to check
+ * @samples: sampling rate
*
- * Returns the byte size of the given samples for the format, or a
+ * Return: The byte size of the given samples for the format, or a
* negative error code if unknown format.
*/
ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
@@ -338,15 +366,15 @@ EXPORT_SYMBOL(snd_pcm_format_size);
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
* @format: the format to check
*
- * Returns the format pattern to fill or NULL if error.
+ * Return: The format pattern to fill or %NULL if error.
*/
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
{
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
return NULL;
- if (! pcm_formats[format].phys)
+ if (! pcm_formats[(INT)format].phys)
return NULL;
- return pcm_formats[format].silence;
+ return pcm_formats[(INT)format].silence;
}
EXPORT_SYMBOL(snd_pcm_format_silence_64);
@@ -359,23 +387,23 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64);
*
* Sets the silence data on the buffer for the given samples.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
{
int width;
unsigned char *dst, *pat;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
return -EINVAL;
if (samples == 0)
return 0;
- width = pcm_formats[format].phys; /* physical width */
- pat = pcm_formats[format].silence;
+ width = pcm_formats[(INT)format].phys; /* physical width */
+ pat = pcm_formats[(INT)format].silence;
if (! width)
return -EINVAL;
/* signed or 1 byte data */
- if (pcm_formats[format].signd == 1 || width <= 8) {
+ if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
unsigned int bytes = samples * width / 8;
memset(data, *pat, bytes);
return 0;
@@ -422,38 +450,6 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
EXPORT_SYMBOL(snd_pcm_format_set_silence);
-/* [width][unsigned][bigendian] */
-static int linear_formats[4][2][2] = {
- {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8},
- { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}},
- {{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE},
- {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}},
- {{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE},
- {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}},
- {{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE},
- {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}}
-};
-
-/**
- * snd_pcm_build_linear_format - return the suitable linear format for the given condition
- * @width: the bit-width
- * @unsignd: 1 if unsigned, 0 if signed.
- * @big_endian: 1 if big-endian, 0 if little-endian
- *
- * Returns the suitable linear format for the given condition.
- */
-snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian)
-{
- if (width & 7)
- return SND_PCM_FORMAT_UNKNOWN;
- width = (width / 8) - 1;
- if (width < 0 || width >= 4)
- return SND_PCM_FORMAT_UNKNOWN;
- return linear_formats[width][!!unsignd][!!big_endian];
-}
-
-EXPORT_SYMBOL(snd_pcm_build_linear_format);
-
/**
* snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
* @runtime: the runtime instance
@@ -461,25 +457,20 @@ EXPORT_SYMBOL(snd_pcm_build_linear_format);
* 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)
{
- static unsigned rates[] = {
- /* ATTENTION: these values depend on the definition in pcm.h! */
- 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
- 64000, 88200, 96000, 176400, 192000
- };
int i;
- for (i = 0; i < (int)ARRAY_SIZE(rates); i++) {
+ for (i = 0; i < (int)snd_pcm_known_rates.count; i++) {
if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_min = rates[i];
+ runtime->hw.rate_min = snd_pcm_known_rates.list[i];
break;
}
}
- for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) {
+ for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) {
if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_max = rates[i];
+ runtime->hw.rate_max = snd_pcm_known_rates.list[i];
break;
}
}
@@ -487,3 +478,78 @@ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
}
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
+ *
+ * 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)
+{
+ unsigned int i;
+
+ for (i = 0; i < snd_pcm_known_rates.count; i++)
+ if (snd_pcm_known_rates.list[i] == rate)
+ return 1u << i;
+ 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 6ea67b16c67..b653ab001fb 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,14 +19,14 @@
*
*/
-#include <sound/driver.h>
#include <linux/mm.h>
-#include <linux/smp_lock.h>
+#include <linux/module.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
-#include <linux/latency.h>
-#include <linux/uio.h>
+#include <linux/pm_qos.h>
+#include <linux/aio.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -35,6 +35,9 @@
#include <sound/timer.h>
#include <sound/minors.h>
#include <asm/io.h>
+#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+#include <dma-coherence.h>
+#endif
/*
* Compatibility
@@ -96,7 +99,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
- snd_assert(substream != NULL, return -ENXIO);
memset(info, 0, sizeof(*info));
info->card = pcm->card->number;
info->device = pcm->device;
@@ -140,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
#ifdef RULES_DEBUG
#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
-char *snd_pcm_hw_param_names[] = {
+static const char * const snd_pcm_hw_param_names[] = {
HW_PARAM(ACCESS),
HW_PARAM(FORMAT),
HW_PARAM(SUBFORMAT),
@@ -188,12 +190,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
if (!(params->rmask & (1 << k)))
continue;
#ifdef RULES_DEBUG
- printk("%s = ", snd_pcm_hw_param_names[k]);
- printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+ pr_debug("%s = ", snd_pcm_hw_param_names[k]);
+ pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif
changed = snd_mask_refine(m, constrs_mask(constrs, k));
#ifdef RULES_DEBUG
- printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+ pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif
if (changed)
params->cmask |= 1 << k;
@@ -208,21 +210,21 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
if (!(params->rmask & (1 << k)))
continue;
#ifdef RULES_DEBUG
- printk("%s = ", snd_pcm_hw_param_names[k]);
+ pr_debug("%s = ", snd_pcm_hw_param_names[k]);
if (i->empty)
- printk("empty");
+ pr_cont("empty");
else
- printk("%c%u %u%c",
+ pr_cont("%c%u %u%c",
i->openmin ? '(' : '[', i->min,
i->max, i->openmax ? ')' : ']');
- printk(" -> ");
+ pr_cont(" -> ");
#endif
changed = snd_interval_refine(i, constrs_interval(constrs, k));
#ifdef RULES_DEBUG
if (i->empty)
- printk("empty\n");
+ pr_cont("empty\n");
else
- printk("%c%u %u%c\n",
+ pr_cont("%c%u %u%c\n",
i->openmin ? '(' : '[', i->min,
i->max, i->openmax ? ')' : ']');
#endif
@@ -253,18 +255,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
if (!doit)
continue;
#ifdef RULES_DEBUG
- printk("Rule %d [%p]: ", k, r->func);
+ pr_debug("Rule %d [%p]: ", k, r->func);
if (r->var >= 0) {
- printk("%s = ", snd_pcm_hw_param_names[r->var]);
+ pr_cont("%s = ", snd_pcm_hw_param_names[r->var]);
if (hw_is_mask(r->var)) {
m = hw_param_mask(params, r->var);
- printk("%x", *m->bits);
+ pr_cont("%x", *m->bits);
} else {
i = hw_param_interval(params, r->var);
if (i->empty)
- printk("empty");
+ pr_cont("empty");
else
- printk("%c%u %u%c",
+ pr_cont("%c%u %u%c",
i->openmin ? '(' : '[', i->min,
i->max, i->openmax ? ')' : ']');
}
@@ -273,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) {
@@ -314,9 +316,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
hw = &substream->runtime->hw;
if (!params->info)
- params->info = hw->info;
- if (!params->fifo_size)
- params->fifo_size = hw->fifo_size;
+ params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+ if (!params->fifo_size) {
+ m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (snd_mask_min(m) == snd_mask_max(m) &&
+ snd_interval_min(i) == snd_interval_max(i)) {
+ changed = substream->ops->ioctl(substream,
+ SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
+ if (changed < 0)
+ return changed;
+ }
+ }
params->rmask = 0;
return 0;
}
@@ -329,21 +340,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params;
int err;
- params = kmalloc(sizeof(*params), GFP_KERNEL);
- if (!params) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(params, _params, sizeof(*params))) {
- err = -EFAULT;
- goto out;
- }
+ params = memdup_user(_params, sizeof(*params));
+ if (IS_ERR(params))
+ return PTR_ERR(params);
+
err = snd_pcm_hw_refine(substream, params);
if (copy_to_user(_params, params, sizeof(*params))) {
if (!err)
err = -EFAULT;
}
-out:
+
kfree(params);
return err;
}
@@ -363,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)
{
@@ -371,9 +385,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int bits;
snd_pcm_uframes_t frames;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_OPEN:
@@ -385,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))
@@ -414,10 +428,12 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->period_size = params_period_size(params);
runtime->periods = params_periods(params);
runtime->buffer_size = params_buffer_size(params);
- runtime->tick_time = params_tick_time(params);
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;
@@ -434,9 +450,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
/* Default sw params */
runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
runtime->period_step = 1;
- runtime->sleep_min = 0;
runtime->control->avail_min = runtime->period_size;
- runtime->xfer_align = runtime->period_size;
runtime->start_threshold = 1;
runtime->stop_threshold = runtime->buffer_size;
runtime->silence_threshold = 0;
@@ -446,17 +460,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->boundary *= 2;
snd_pcm_timer_resolution_change(substream);
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
- remove_acceptable_latency(substream->latency_id);
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
if ((usecs = period_to_usecs(runtime)) >= 0)
- set_acceptable_latency(substream->latency_id, usecs);
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY, usecs);
return 0;
_error:
- /* hardware might be unuseable from this time,
+ /* hardware might be unusable from this time,
so we force application to retry to set
the correct hardware parameter settings */
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream);
return err;
@@ -468,21 +484,16 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params;
int err;
- params = kmalloc(sizeof(*params), GFP_KERNEL);
- if (!params) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(params, _params, sizeof(*params))) {
- err = -EFAULT;
- goto out;
- }
+ params = memdup_user(_params, sizeof(*params));
+ if (IS_ERR(params))
+ return PTR_ERR(params);
+
err = snd_pcm_hw_params(substream, params);
if (copy_to_user(_params, params, sizeof(*params))) {
if (!err)
err = -EFAULT;
}
-out:
+
kfree(params);
return err;
}
@@ -492,9 +503,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
int result = 0;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_SETUP:
@@ -509,8 +520,8 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
return -EBADFD;
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
- remove_acceptable_latency(substream->latency_id);
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
return result;
}
@@ -518,10 +529,11 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
struct snd_pcm_sw_params *params)
{
struct snd_pcm_runtime *runtime;
+ int err;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -ENXIO);
snd_pcm_stream_lock_irq(substream);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_pcm_stream_unlock_irq(substream);
@@ -533,9 +545,6 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
return -EINVAL;
if (params->avail_min == 0)
return -EINVAL;
- if (params->xfer_align == 0 ||
- params->xfer_align % runtime->min_align != 0)
- return -EINVAL;
if (params->silence_size >= runtime->boundary) {
if (params->silence_threshold != 0)
return -EINVAL;
@@ -545,29 +554,24 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
if (params->silence_threshold > runtime->buffer_size)
return -EINVAL;
}
+ err = 0;
snd_pcm_stream_lock_irq(substream);
runtime->tstamp_mode = params->tstamp_mode;
- runtime->sleep_min = params->sleep_min;
runtime->period_step = params->period_step;
runtime->control->avail_min = params->avail_min;
runtime->start_threshold = params->start_threshold;
runtime->stop_threshold = params->stop_threshold;
runtime->silence_threshold = params->silence_threshold;
runtime->silence_size = params->silence_size;
- runtime->xfer_align = params->xfer_align;
params->boundary = runtime->boundary;
if (snd_pcm_running(substream)) {
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
- else
- snd_pcm_tick_set(substream, 0);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- wake_up(&runtime->sleep);
+ err = snd_pcm_update_state(substream, runtime);
}
snd_pcm_stream_unlock_irq(substream);
- return 0;
+ return err;
}
static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
@@ -596,25 +600,29 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
status->trigger_tstamp = runtime->trigger_tstamp;
if (snd_pcm_running(substream)) {
snd_pcm_update_hw_ptr(substream);
- if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp;
- else
- getnstimeofday(&status->tstamp);
- } else
- getnstimeofday(&status->tstamp);
+ status->audio_tstamp =
+ runtime->status->audio_tstamp;
+ goto _tstamp_end;
+ }
+ }
+ snd_pcm_gettime(runtime, &status->tstamp);
+ _tstamp_end:
status->appl_ptr = runtime->control->appl_ptr;
status->hw_ptr = runtime->status->hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
status->avail = snd_pcm_playback_avail(runtime);
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
- runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
status->delay = runtime->buffer_size - status->avail;
- else
+ status->delay += runtime->delay;
+ } else
status->delay = 0;
} else {
status->avail = snd_pcm_capture_avail(runtime);
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- status->delay = status->avail;
+ status->delay = status->avail + runtime->delay;
else
status->delay = 0;
}
@@ -631,11 +639,8 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
struct snd_pcm_status __user * _status)
{
struct snd_pcm_status status;
- struct snd_pcm_runtime *runtime;
int res;
- snd_assert(substream != NULL, return -ENXIO);
- runtime = substream->runtime;
memset(&status, 0, sizeof(status));
res = snd_pcm_status(substream, &status);
if (res < 0)
@@ -651,7 +656,6 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime;
unsigned int channel;
- snd_assert(substream != NULL, return -ENXIO);
channel = info->channel;
runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
@@ -689,7 +693,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
if (runtime->trigger_master == NULL)
return;
if (runtime->trigger_master == substream) {
- getnstimeofday(&runtime->trigger_tstamp);
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
} else {
snd_pcm_trigger_tstamp(runtime->trigger_master);
runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
@@ -713,26 +717,23 @@ static int snd_pcm_action_group(struct action_ops *ops,
struct snd_pcm_substream *substream,
int state, int do_lock)
{
- struct list_head *pos;
struct snd_pcm_substream *s = NULL;
struct snd_pcm_substream *s1;
int res = 0;
- snd_pcm_group_for_each(pos, substream) {
- s = snd_pcm_group_substream_entry(pos);
+ snd_pcm_group_for_each_entry(s, substream) {
if (do_lock && s != substream)
- spin_lock(&s->self_group.lock);
+ spin_lock_nested(&s->self_group.lock,
+ SINGLE_DEPTH_NESTING);
res = ops->pre_action(s, state);
if (res < 0)
goto _unlock;
}
- snd_pcm_group_for_each(pos, substream) {
- s = snd_pcm_group_substream_entry(pos);
+ snd_pcm_group_for_each_entry(s, substream) {
res = ops->do_action(s, state);
if (res < 0) {
if (ops->undo_action) {
- snd_pcm_group_for_each(pos, substream) {
- s1 = snd_pcm_group_substream_entry(pos);
+ snd_pcm_group_for_each_entry(s1, substream) {
if (s1 == s) /* failed stream */
break;
ops->undo_action(s1, state);
@@ -742,15 +743,13 @@ static int snd_pcm_action_group(struct action_ops *ops,
goto _unlock;
}
}
- snd_pcm_group_for_each(pos, substream) {
- s = snd_pcm_group_substream_entry(pos);
+ snd_pcm_group_for_each_entry(s, substream) {
ops->post_action(s, state);
}
_unlock:
if (do_lock) {
/* unlock streams */
- snd_pcm_group_for_each(pos, substream) {
- s1 = snd_pcm_group_substream_entry(pos);
+ snd_pcm_group_for_each_entry(s1, substream) {
if (s1 != substream)
spin_unlock(&s1->self_group.lock);
if (s1 == s) /* end */
@@ -877,12 +876,13 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
+ runtime->hw_ptr_jiffies = jiffies;
+ runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
+ runtime->rate;
runtime->status->state = state;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
&runtime->trigger_tstamp);
@@ -896,10 +896,10 @@ static struct action_ops snd_pcm_action_start = {
};
/**
- * snd_pcm_start
+ * snd_pcm_start - start all linked streams
* @substream: the PCM substream instance
*
- * Start all linked streams.
+ * Return: Zero if successful, or a negative error code.
*/
int snd_pcm_start(struct snd_pcm_substream *substream)
{
@@ -936,9 +936,9 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
&runtime->trigger_tstamp);
runtime->status->state = state;
- snd_pcm_tick_set(substream, 0);
}
wake_up(&runtime->sleep);
+ wake_up(&runtime->tsleep);
}
static struct action_ops snd_pcm_action_stop = {
@@ -948,14 +948,15 @@ static struct action_ops snd_pcm_action_stop = {
};
/**
- * snd_pcm_stop
+ * snd_pcm_stop - try to stop all running streams in the substream group
* @substream: the PCM substream instance
* @state: PCM state after stopping the stream
*
- * Try to stop all running streams in the substream group.
- * The state of each stream is changed to the given value after that unconditionally.
+ * The state of each stream is then changed to the given state unconditionally.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
+int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
{
return snd_pcm_action(&snd_pcm_action_stop, substream, state);
}
@@ -963,12 +964,13 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
EXPORT_SYMBOL(snd_pcm_stop);
/**
- * snd_pcm_drain_done
+ * snd_pcm_drain_done - stop the DMA only when the given stream is playback
* @substream: the PCM substream
*
- * Stop the DMA only when the given stream is playback.
- * The state is changed to SETUP.
+ * After stopping, the state is changed to SETUP.
* Unlike snd_pcm_stop(), this affects only the given stream.
+ *
+ * Return: Zero if succesful, or a negative error code.
*/
int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
@@ -997,6 +999,15 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
{
if (substream->runtime->trigger_master != substream)
return 0;
+ /* some drivers might use hw_ptr to recover from the pause -
+ update the hw_ptr now */
+ if (push)
+ snd_pcm_update_hw_ptr(substream);
+ /* The jiffies check in snd_pcm_update_hw_ptr*() is done by
+ * a delta between the current jiffies, this gives a large enough
+ * delta, effectively to skip the check once.
+ */
+ substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
return substream->ops->trigger(substream,
push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
@@ -1020,12 +1031,10 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
snd_timer_notify(substream->timer,
SNDRV_TIMER_EVENT_MPAUSE,
&runtime->trigger_tstamp);
- snd_pcm_tick_set(substream, 0);
wake_up(&runtime->sleep);
+ wake_up(&runtime->tsleep);
} else {
runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
if (substream->timer)
snd_timer_notify(substream->timer,
SNDRV_TIMER_EVENT_MCONTINUE,
@@ -1080,8 +1089,8 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
&runtime->trigger_tstamp);
runtime->status->suspended_state = runtime->status->state;
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
- snd_pcm_tick_set(substream, 0);
wake_up(&runtime->sleep);
+ wake_up(&runtime->tsleep);
}
static struct action_ops snd_pcm_action_suspend = {
@@ -1091,11 +1100,13 @@ static struct action_ops snd_pcm_action_suspend = {
};
/**
- * snd_pcm_suspend
+ * snd_pcm_suspend - trigger SUSPEND to all linked streams
* @substream: the PCM substream
*
- * Trigger SUSPEND to all linked streams.
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @substream is %NULL), or a negative error
+ * code.
*/
int snd_pcm_suspend(struct snd_pcm_substream *substream)
{
@@ -1114,11 +1125,12 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream)
EXPORT_SYMBOL(snd_pcm_suspend);
/**
- * snd_pcm_suspend_all
+ * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
* @pcm: the PCM instance
*
- * Trigger SUSPEND to all substreams in the given pcm.
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @pcm is %NULL), or a negative error code.
*/
int snd_pcm_suspend_all(struct snd_pcm *pcm)
{
@@ -1183,8 +1195,6 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
&runtime->trigger_tstamp);
runtime->status->state = runtime->status->suspended_state;
- if (runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
}
static struct action_ops snd_pcm_action_resume = {
@@ -1273,7 +1283,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
if (err < 0)
return err;
- // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
@@ -1332,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 = {
@@ -1342,11 +1351,11 @@ static struct action_ops snd_pcm_action_prepare = {
};
/**
- * snd_pcm_prepare
+ * snd_pcm_prepare - prepare the PCM substream to be triggerable
* @substream: the PCM substream instance
* @file: file to refer f_flags
*
- * Prepare the PCM substream to be triggerable.
+ * Return: Zero if successful, or a negative error code.
*/
static int snd_pcm_prepare(struct snd_pcm_substream *substream,
struct file *file)
@@ -1374,9 +1383,14 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
{
- if (substream->f_flags & O_NONBLOCK)
- return -EAGAIN;
- substream->runtime->trigger_master = substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBADFD;
+ }
+ runtime->trigger_master = substream;
return 0;
}
@@ -1395,16 +1409,19 @@ 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;
}
} else {
/* stop running stream */
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
- int state = snd_pcm_capture_avail(runtime) > 0 ?
+ int new_state = snd_pcm_capture_avail(runtime) > 0 ?
SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
- snd_pcm_do_stop(substream, state);
- snd_pcm_post_stop(substream, state);
+ snd_pcm_do_stop(substream, new_state);
+ snd_pcm_post_stop(substream, new_state);
}
}
return 0;
@@ -1420,12 +1437,6 @@ static struct action_ops snd_pcm_action_drain_init = {
.post_action = snd_pcm_post_drain_init
};
-struct drain_rec {
- struct snd_pcm_substream *substream;
- wait_queue_t wait;
- snd_pcm_uframes_t stop_threshold;
-};
-
static int snd_pcm_drop(struct snd_pcm_substream *substream);
/*
@@ -1435,16 +1446,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream);
* After this call, all streams are supposed to be either SETUP or DRAINING
* (capture only) state.
*/
-static int snd_pcm_drain(struct snd_pcm_substream *substream)
+static int snd_pcm_drain(struct snd_pcm_substream *substream,
+ struct file *file)
{
struct snd_card *card;
struct snd_pcm_runtime *runtime;
- struct list_head *pos;
+ struct snd_pcm_substream *s;
+ wait_queue_t wait;
int result = 0;
- int i, num_drecs;
- struct drain_rec *drec, drec_tmp, *d;
+ int nonblock = 0;
- snd_assert(substream != NULL, return -ENXIO);
card = substream->pcm->card;
runtime = substream->runtime;
@@ -1460,38 +1471,13 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
}
}
- /* allocate temporary record for drain sync */
- down_read(&snd_pcm_link_rwsem);
- if (snd_pcm_stream_linked(substream)) {
- drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
- if (! drec) {
- up_read(&snd_pcm_link_rwsem);
- snd_power_unlock(card);
- return -ENOMEM;
- }
- } else
- drec = &drec_tmp;
-
- /* count only playback streams */
- num_drecs = 0;
- snd_pcm_group_for_each(pos, substream) {
- struct snd_pcm_substream *s = snd_pcm_group_substream_entry(pos);
- runtime = s->runtime;
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- d = &drec[num_drecs++];
- d->substream = s;
- init_waitqueue_entry(&d->wait, current);
- add_wait_queue(&runtime->sleep, &d->wait);
- /* stop_threshold fixup to avoid endless loop when
- * stop_threshold > buffer_size
- */
- d->stop_threshold = runtime->stop_threshold;
- if (runtime->stop_threshold > runtime->buffer_size)
- runtime->stop_threshold = runtime->buffer_size;
- }
- }
- up_read(&snd_pcm_link_rwsem);
+ if (file) {
+ if (file->f_flags & O_NONBLOCK)
+ nonblock = 1;
+ } else if (substream->f_flags & O_NONBLOCK)
+ nonblock = 1;
+ down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
/* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1499,37 +1485,64 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
/* pre-start/stop - all running streams are changed to DRAINING state */
result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
- if (result < 0) {
- snd_pcm_stream_unlock_irq(substream);
- goto _error;
+ if (result < 0)
+ goto unlock;
+ /* in non-blocking, we don't wait in ioctl but let caller poll */
+ if (nonblock) {
+ result = -EAGAIN;
+ goto unlock;
}
for (;;) {
long tout;
+ struct snd_pcm_runtime *to_check;
if (signal_pending(current)) {
result = -ERESTARTSYS;
break;
}
- /* all finished? */
- for (i = 0; i < num_drecs; i++) {
- runtime = drec[i].substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ /* find a substream to drain */
+ to_check = NULL;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ continue;
+ runtime = s->runtime;
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ to_check = runtime;
break;
+ }
}
- if (i == num_drecs)
- break; /* yes, all drained */
-
- set_current_state(TASK_INTERRUPTIBLE);
+ if (!to_check)
+ break; /* all drained */
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&to_check->sleep, &wait);
snd_pcm_stream_unlock_irq(substream);
+ up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
- tout = schedule_timeout(10 * HZ);
+ if (runtime->no_period_wakeup)
+ tout = MAX_SCHEDULE_TIMEOUT;
+ else {
+ tout = 10;
+ if (runtime->rate) {
+ long t = runtime->period_size * 2 / runtime->rate;
+ tout = max(t, tout);
+ }
+ tout = msecs_to_jiffies(tout * 1000);
+ }
+ tout = schedule_timeout_interruptible(tout);
snd_power_lock(card);
+ down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
+ remove_wait_queue(&to_check->sleep, &wait);
+ if (card->shutdown) {
+ result = -ENODEV;
+ break;
+ }
if (tout == 0) {
if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
result = -ESTRPIPE;
else {
- snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+ dev_dbg(substream->pcm->card->dev,
+ "playback drain error (DMA or IRQ trouble?)\n");
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
result = -EIO;
}
@@ -1537,18 +1550,9 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
}
}
+ unlock:
snd_pcm_stream_unlock_irq(substream);
-
- _error:
- for (i = 0; i < num_drecs; i++) {
- d = &drec[i];
- runtime = d->substream->runtime;
- remove_wait_queue(&runtime->sleep, &d->wait);
- runtime->stop_threshold = d->stop_threshold;
- }
-
- if (drec != &drec_tmp)
- kfree(drec);
+ up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
return result;
@@ -1562,24 +1566,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
static int snd_pcm_drop(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- struct snd_card *card;
int result = 0;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- card = substream->pcm->card;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
return -EBADFD;
- snd_power_lock(card);
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result < 0)
- goto _unlock;
- }
-
snd_pcm_stream_lock_irq(substream);
/* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1588,35 +1585,21 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
snd_pcm_stream_unlock_irq(substream);
- _unlock:
- snd_power_unlock(card);
+
return result;
}
-/* WARNING: Don't forget to fput back the file */
-static struct file *snd_pcm_file_fd(int fd)
+static bool is_pcm_file(struct file *file)
{
- struct file *file;
- struct inode *inode;
+ struct inode *inode = file_inode(file);
unsigned int minor;
- file = fget(fd);
- if (!file)
- return NULL;
- inode = file->f_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);
}
/*
@@ -1625,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 ||
@@ -1646,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);
@@ -1662,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;
}
@@ -1675,7 +1668,7 @@ static void relink_to_local(struct snd_pcm_substream *substream)
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
{
- struct list_head *pos;
+ struct snd_pcm_substream *s;
int res = 0;
down_write(&snd_pcm_link_rwsem);
@@ -1687,8 +1680,8 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
list_del(&substream->link_list);
substream->group->count--;
if (substream->group->count == 1) { /* detach the last stream, too */
- snd_pcm_group_for_each(pos, substream) {
- relink_to_local(snd_pcm_group_substream_entry(pos));
+ snd_pcm_group_for_each_entry(s, substream) {
+ relink_to_local(s);
break;
}
kfree(substream->group);
@@ -1794,12 +1787,18 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
48000, 64000, 88200, 96000, 176400, 192000 };
+const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+};
+
static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_pcm_hardware *hw = rule->private;
return snd_interval_list(hw_param_interval(params, rule->var),
- ARRAY_SIZE(rates), rates, hw->rates);
+ snd_pcm_known_rates.count,
+ snd_pcm_known_rates.list, hw->rates);
}
static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
@@ -1959,33 +1958,41 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
}
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
hw->channels_min, hw->channels_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
hw->rate_min, hw->rate_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
hw->period_bytes_min, hw->period_bytes_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
hw->periods_min, hw->periods_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
hw->period_bytes_min, hw->buffer_bytes_max);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
snd_pcm_hw_rule_buffer_bytes_max, substream,
@@ -1996,7 +2003,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
/* FIXME: remove */
if (runtime->dma_bytes) {
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
- snd_assert(err >= 0, return -EINVAL);
+ if (err < 0)
+ return err;
}
if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
@@ -2008,8 +2016,6 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
}
/* FIXME: this belong to lowlevel */
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
- 1000000 / HZ, 1000000 / HZ);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return 0;
@@ -2033,6 +2039,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
substream->ops->close(substream);
substream->hw_opened = 0;
}
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
if (substream->pcm_release) {
substream->pcm_release(substream);
substream->pcm_release = NULL;
@@ -2059,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;
}
@@ -2070,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;
}
@@ -2086,17 +2094,12 @@ EXPORT_SYMBOL(snd_pcm_open_substream);
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
- int stream,
- struct snd_pcm_file **rpcm_file)
+ int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
- struct snd_pcm_str *str;
int err;
- snd_assert(rpcm_file != NULL, return -EINVAL);
- *rpcm_file = NULL;
-
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
@@ -2108,37 +2111,45 @@ static int snd_pcm_open_file(struct file *file,
}
pcm_file->substream = substream;
if (substream->ref_count == 1) {
- str = substream->pstr;
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file;
- *rpcm_file = pcm_file;
+
return 0;
}
static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
-
+ int err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
-
+ int err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
- struct snd_pcm_file *pcm_file;
wait_queue_t wait;
if (pcm == NULL) {
@@ -2156,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) {
@@ -2170,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;
@@ -2197,9 +2212,9 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
pcm = substream->pcm;
- fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex);
snd_pcm_release_substream(substream);
kfree(pcm_file);
@@ -2233,6 +2248,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2245,15 +2263,10 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
}
if (frames > (snd_pcm_uframes_t)hw_avail)
frames = hw_avail;
- else
- frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr - frames;
if (appl_ptr < 0)
appl_ptr += runtime->boundary;
runtime->control->appl_ptr = appl_ptr;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
- runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
ret = frames;
__end:
snd_pcm_stream_unlock_irq(substream);
@@ -2283,6 +2296,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2295,15 +2311,10 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
}
if (frames > (snd_pcm_uframes_t)hw_avail)
frames = hw_avail;
- else
- frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr - frames;
if (appl_ptr < 0)
appl_ptr += runtime->boundary;
runtime->control->appl_ptr = appl_ptr;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
- runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
ret = frames;
__end:
snd_pcm_stream_unlock_irq(substream);
@@ -2334,6 +2345,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2346,15 +2360,10 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
}
if (frames > (snd_pcm_uframes_t)avail)
frames = avail;
- else
- frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr + frames;
if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
appl_ptr -= runtime->boundary;
runtime->control->appl_ptr = appl_ptr;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
- runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
ret = frames;
__end:
snd_pcm_stream_unlock_irq(substream);
@@ -2385,6 +2394,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2397,15 +2409,10 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
}
if (frames > (snd_pcm_uframes_t)avail)
frames = avail;
- else
- frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr + frames;
if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
appl_ptr -= runtime->boundary;
runtime->control->appl_ptr = appl_ptr;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
- runtime->sleep_min)
- snd_pcm_tick_prepare(substream);
ret = frames;
__end:
snd_pcm_stream_unlock_irq(substream);
@@ -2422,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;
@@ -2454,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;
@@ -2465,6 +2474,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
n = snd_pcm_playback_hw_avail(runtime);
else
n = snd_pcm_capture_avail(runtime);
+ n += runtime->delay;
break;
case SNDRV_PCM_STATE_XRUN:
err = -EPIPE;
@@ -2520,20 +2530,35 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
return -EFAULT;
return 0;
}
+
+static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int arg;
+
+ if (get_user(arg, _arg))
+ return -EFAULT;
+ if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
+ return -EINVAL;
+ runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
+ if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
+ runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+ return 0;
+}
static int snd_pcm_common_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
-
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
case SNDRV_PCM_IOCTL_INFO:
return snd_pcm_info_user(substream, arg);
- case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
+ case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
return 0;
+ case SNDRV_PCM_IOCTL_TTSTAMP:
+ return snd_pcm_tstamp(substream, arg);
case SNDRV_PCM_IOCTL_HW_REFINE:
return snd_pcm_hw_refine_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS:
@@ -2573,7 +2598,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
return snd_pcm_hw_params_old_user(substream, arg);
#endif
case SNDRV_PCM_IOCTL_DRAIN:
- return snd_pcm_drain(substream);
+ return snd_pcm_drain(substream, file);
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
@@ -2585,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;
}
@@ -2593,8 +2618,10 @@ static int snd_pcm_playback_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
- snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
+ if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
+ return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
{
@@ -2627,13 +2654,11 @@ static int snd_pcm_playback_ioctl1(struct file *file,
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;
- bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
- if (bufs == NULL)
- return -ENOMEM;
- if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
- kfree(bufs);
- return -EFAULT;
- }
+
+ bufs = memdup_user(xfern.bufs,
+ sizeof(void *) * runtime->channels);
+ if (IS_ERR(bufs))
+ return PTR_ERR(bufs);
result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
@@ -2673,8 +2698,10 @@ static int snd_pcm_capture_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
- snd_assert(substream != NULL, return -ENXIO);
- snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+ if (snd_BUG_ON(!substream))
+ return -ENXIO;
+ if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE))
+ return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_READI_FRAMES:
{
@@ -2707,13 +2734,11 @@ static int snd_pcm_capture_ioctl1(struct file *file,
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;
- bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
- if (bufs == NULL)
- return -ENOMEM;
- if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
- kfree(bufs);
- return -EFAULT;
- }
+
+ bufs = memdup_user(xfern.bufs,
+ sizeof(void *) * runtime->channels);
+ if (IS_ERR(bufs))
+ return PTR_ERR(bufs);
result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
@@ -2813,7 +2838,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2836,21 +2862,17 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, result = -ENXIO; goto end);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- result = -EBADFD;
- goto end;
- }
- if (!frame_aligned(runtime, count)) {
- result = -EINVAL;
- goto end;
- }
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (!frame_aligned(runtime, count))
+ return -EINVAL;
count = bytes_to_frames(runtime, count);
result = snd_pcm_lib_write(substream, buf, count);
if (result > 0)
result = frames_to_bytes(runtime, result);
- end:
return result;
}
@@ -2868,7 +2890,8 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2902,17 +2925,14 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, result = -ENXIO; goto end);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- result = -EBADFD;
- goto end;
- }
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
if (nr_segs > 128 || nr_segs != runtime->channels ||
- !frame_aligned(runtime, iov->iov_len)) {
- result = -EINVAL;
- goto end;
- }
+ !frame_aligned(runtime, iov->iov_len))
+ return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
if (bufs == NULL)
@@ -2923,7 +2943,6 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (result > 0)
result = frames_to_bytes(runtime, result);
kfree(bufs);
- end:
return result;
}
@@ -2938,7 +2957,8 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
@@ -2976,7 +2996,8 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
@@ -3019,86 +3040,74 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
/*
* mmap status record
*/
-static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area,
- unsigned long address, int *type)
+static int snd_pcm_mmap_status_fault(struct vm_area_struct *area,
+ struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = area->vm_private_data;
struct snd_pcm_runtime *runtime;
- struct page * page;
if (substream == NULL)
- return NOPAGE_SIGBUS;
+ return VM_FAULT_SIGBUS;
runtime = substream->runtime;
- page = virt_to_page(runtime->status);
- get_page(page);
- if (type)
- *type = VM_FAULT_MINOR;
- return page;
+ vmf->page = virt_to_page(runtime->status);
+ get_page(vmf->page);
+ return 0;
}
-static struct vm_operations_struct snd_pcm_vm_ops_status =
+static const struct vm_operations_struct snd_pcm_vm_ops_status =
{
- .nopage = snd_pcm_mmap_status_nopage,
+ .fault = snd_pcm_mmap_status_fault,
};
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_status;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
return 0;
}
/*
* mmap control record
*/
-static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area,
- unsigned long address, int *type)
+static int snd_pcm_mmap_control_fault(struct vm_area_struct *area,
+ struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = area->vm_private_data;
struct snd_pcm_runtime *runtime;
- struct page * page;
if (substream == NULL)
- return NOPAGE_SIGBUS;
+ return VM_FAULT_SIGBUS;
runtime = substream->runtime;
- page = virt_to_page(runtime->control);
- get_page(page);
- if (type)
- *type = VM_FAULT_MINOR;
- return page;
+ vmf->page = virt_to_page(runtime->control);
+ get_page(vmf->page);
+ return 0;
}
-static struct vm_operations_struct snd_pcm_vm_ops_control =
+static const struct vm_operations_struct snd_pcm_vm_ops_control =
{
- .nopage = snd_pcm_mmap_control_nopage,
+ .fault = snd_pcm_mmap_control_fault,
};
static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_control;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
return 0;
}
#else /* ! coherent mmap */
@@ -3117,92 +3126,120 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
+static inline struct page *
+snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
+{
+ void *vaddr = substream->runtime->dma_area + ofs;
+#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return virt_to_page(CAC_ADDR(vaddr));
+#endif
+#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
+ dma_addr_t addr = substream->runtime->dma_addr + ofs;
+ addr -= get_dma_offset(substream->dma_buffer.dev.dev);
+ /* assume dma_handle set via pfn_to_phys() in
+ * mm/dma-noncoherent.c
+ */
+ return pfn_to_page(addr >> PAGE_SHIFT);
+ }
+#endif
+ return virt_to_page(vaddr);
+}
+
/*
- * nopage callback for mmapping a RAM page
+ * fault callback for mmapping a RAM page
*/
-static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
- unsigned long address, int *type)
+static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
+ struct vm_fault *vmf)
{
struct snd_pcm_substream *substream = area->vm_private_data;
struct snd_pcm_runtime *runtime;
unsigned long offset;
struct page * page;
- void *vaddr;
size_t dma_bytes;
if (substream == NULL)
- return NOPAGE_SIGBUS;
+ return VM_FAULT_SIGBUS;
runtime = substream->runtime;
- offset = area->vm_pgoff << PAGE_SHIFT;
- offset += address - area->vm_start;
- snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
+ offset = vmf->pgoff << PAGE_SHIFT;
dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
if (offset > dma_bytes - PAGE_SIZE)
- return NOPAGE_SIGBUS;
- if (substream->ops->page) {
+ return VM_FAULT_SIGBUS;
+ if (substream->ops->page)
page = substream->ops->page(substream, offset);
- if (! page)
- return NOPAGE_OOM; /* XXX: is this really due to OOM? */
- } else {
- vaddr = runtime->dma_area + offset;
- page = virt_to_page(vaddr);
- }
+ else
+ page = snd_pcm_default_page_ops(substream, offset);
+ if (!page)
+ return VM_FAULT_SIGBUS;
get_page(page);
- if (type)
- *type = VM_FAULT_MINOR;
- return page;
+ vmf->page = page;
+ return 0;
}
-static struct vm_operations_struct snd_pcm_vm_ops_data =
-{
+static const struct vm_operations_struct snd_pcm_vm_ops_data = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.open = snd_pcm_mmap_data_open,
.close = snd_pcm_mmap_data_close,
- .nopage = snd_pcm_mmap_data_nopage,
+ .fault = snd_pcm_mmap_data_fault,
};
+#ifndef ARCH_HAS_DMA_MMAP_COHERENT
+/* This should be defined / handled globally! */
+#ifdef CONFIG_ARM
+#define ARCH_HAS_DMA_MMAP_COHERENT
+#endif
+#endif
+
/*
* mmap the DMA buffer on RAM
*/
-static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
-{
- area->vm_ops = &snd_pcm_vm_ops_data;
- area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
- atomic_inc(&substream->mmap_count);
+int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return remap_pfn_range(area, area->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ area->vm_end - area->vm_start, area->vm_page_prot);
+ }
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+ if (!substream->ops->page &&
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return dma_mmap_coherent(substream->dma_buffer.dev.dev,
+ area,
+ substream->runtime->dma_area,
+ substream->runtime->dma_addr,
+ area->vm_end - area->vm_start);
+#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV &&
+ !plat_device_is_coherent(substream->dma_buffer.dev.dev))
+ area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
+#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+ /* mmap with fault handler */
+ area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
/*
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
-static struct vm_operations_struct snd_pcm_vm_ops_data_mmio =
-{
- .open = snd_pcm_mmap_data_open,
- .close = snd_pcm_mmap_data_close,
-};
-
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- long size;
- unsigned long offset;
+ struct snd_pcm_runtime *runtime = substream->runtime;;
-#ifdef pgprot_noncached
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif
- area->vm_ops = &snd_pcm_vm_ops_data_mmio;
- area->vm_private_data = substream;
- area->vm_flags |= VM_IO;
- size = area->vm_end - area->vm_start;
- offset = area->vm_pgoff << PAGE_SHIFT;
- if (io_remap_pfn_range(area, area->vm_start,
- (substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
- size, area->vm_page_prot))
- return -EAGAIN;
- atomic_inc(&substream->mmap_count);
- return 0;
+ return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes);
}
EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
@@ -3218,6 +3255,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
long size;
unsigned long offset;
size_t dma_bytes;
+ int err;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!(area->vm_flags & (VM_WRITE|VM_READ)))
@@ -3227,7 +3265,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
return -EINVAL;
}
runtime = substream->runtime;
- snd_assert(runtime != NULL, return -EAGAIN);
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
@@ -3243,10 +3280,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
if (offset > dma_bytes - size)
return -EINVAL;
+ area->vm_ops = &snd_pcm_vm_ops_data;
+ area->vm_private_data = substream;
if (substream->ops->mmap)
- return substream->ops->mmap(substream, area);
+ err = substream->ops->mmap(substream, area);
else
- return snd_pcm_default_mmap(substream, area);
+ err = snd_pcm_lib_default_mmap(substream, area);
+ if (!err)
+ atomic_inc(&substream->mmap_count);
+ return err;
}
EXPORT_SYMBOL(snd_pcm_mmap_data);
@@ -3259,7 +3301,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
@@ -3282,17 +3325,13 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
struct snd_pcm_file * pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- int err;
pcm_file = file->private_data;
substream = pcm_file->substream;
- snd_assert(substream != NULL, return -ENXIO);
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
runtime = substream->runtime;
-
- err = fasync_helper(fd, file, on, &runtime->fasync);
- if (err < 0)
- return err;
- return 0;
+ return fasync_helper(fd, file, on, &runtime->fasync);
}
/*
@@ -3358,18 +3397,12 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
- if (!params) {
- err = -ENOMEM;
- goto out;
- }
- oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
- if (!oparams) {
- err = -ENOMEM;
- goto out;
- }
+ if (!params)
+ return -ENOMEM;
- if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
- err = -EFAULT;
+ oparams = memdup_user(_oparams, sizeof(*oparams));
+ if (IS_ERR(oparams)) {
+ err = PTR_ERR(oparams);
goto out;
}
snd_pcm_hw_convert_from_old_params(params, oparams);
@@ -3379,9 +3412,10 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
if (!err)
err = -EFAULT;
}
+
+ kfree(oparams);
out:
kfree(params);
- kfree(oparams);
return err;
}
@@ -3393,17 +3427,12 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
- if (!params) {
- err = -ENOMEM;
- goto out;
- }
- oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
- if (!oparams) {
- err = -ENOMEM;
- goto out;
- }
- if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
- err = -EFAULT;
+ if (!params)
+ return -ENOMEM;
+
+ oparams = memdup_user(_oparams, sizeof(*oparams));
+ if (IS_ERR(oparams)) {
+ err = PTR_ERR(oparams);
goto out;
}
snd_pcm_hw_convert_from_old_params(params, oparams);
@@ -3413,29 +3442,57 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
if (!err)
err = -EFAULT;
}
+
+ kfree(oparams);
out:
kfree(params);
- kfree(oparams);
return err;
}
#endif /* CONFIG_SND_SUPPORT_OLD_API */
+#ifndef CONFIG_MMU
+static unsigned long snd_pcm_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ struct snd_pcm_file *pcm_file = file->private_data;
+ struct snd_pcm_substream *substream = pcm_file->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long offset = pgoff << PAGE_SHIFT;
+
+ switch (offset) {
+ case SNDRV_PCM_MMAP_OFFSET_STATUS:
+ return (unsigned long)runtime->status;
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+ return (unsigned long)runtime->control;
+ default:
+ return (unsigned long)runtime->dma_area + offset;
+ }
+}
+#else
+# define snd_pcm_get_unmapped_area NULL
+#endif
+
/*
* Register section
*/
-struct file_operations snd_pcm_f_ops[2] = {
+const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
+ .llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
+ .get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
@@ -3443,10 +3500,12 @@ struct file_operations snd_pcm_f_ops[2] = {
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
+ .llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
+ .get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index d94ed16d21e..20ecd8f1808 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -1,6 +1,6 @@
/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,8 +19,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
+#include <linux/gcd.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/timer.h>
@@ -29,22 +29,6 @@
* Timer functions
*/
-/* Greatest common divisor */
-static unsigned long gcd(unsigned long a, unsigned long b)
-{
- unsigned long r;
- if (a < b) {
- r = a;
- a = b;
- b = r;
- }
- while ((r = a % b) != 0) {
- a = b;
- b = r;
- }
- return b;
-}
-
void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
{
unsigned long rate, mult, fsize, l, post;
@@ -52,12 +36,14 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
mult = 1000000000;
rate = runtime->rate;
- snd_assert(rate != 0, return);
+ if (snd_BUG_ON(!rate))
+ return;
l = gcd(mult, rate);
mult /= l;
rate /= l;
fsize = runtime->period_size;
- snd_assert(fsize != 0, return);
+ if (snd_BUG_ON(!fsize))
+ return;
l = gcd(rate, fsize);
rate /= l;
fsize /= l;
@@ -67,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;
}
@@ -84,25 +72,19 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
static int snd_pcm_timer_start(struct snd_timer * timer)
{
- unsigned long flags;
struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer);
- spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 1;
- spin_unlock_irqrestore(&substream->timer_lock, flags);
return 0;
}
static int snd_pcm_timer_stop(struct snd_timer * timer)
{
- unsigned long flags;
struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer);
- spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 0;
- spin_unlock_irqrestore(&substream->timer_lock, flags);
return 0;
}
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 269c467ca9b..6fc71a4c8a5 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1,6 +1,6 @@
/*
* Abstract layer for MIDI v1.0 stream
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,26 +19,23 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/major.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/slab.h>
#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 <linux/wait.h>
#include <sound/rawmidi.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/minors.h>
#include <sound/initval.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
MODULE_LICENSE("GPL");
@@ -59,16 +56,20 @@ 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 list_head *p;
struct snd_rawmidi *rawmidi;
- list_for_each(p, &snd_rawmidi_devices) {
- rawmidi = list_entry(p, struct snd_rawmidi, list);
+ list_for_each_entry(rawmidi, &snd_rawmidi_devices, list)
if (rawmidi->card == card && rawmidi->device == device)
return rawmidi;
- }
return NULL;
}
@@ -98,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)
@@ -116,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;
@@ -154,19 +145,18 @@ static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up)
{
- if (up) {
- tasklet_hi_schedule(&substream->runtime->tasklet);
- } else {
- tasklet_kill(&substream->runtime->tasklet);
- substream->ops->trigger(substream, 0);
- }
+ if (!substream->opened)
+ return;
+ substream->ops->trigger(substream, up);
}
static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
+ if (!substream->opened)
+ return;
substream->ops->trigger(substream, up);
- if (!up && substream->runtime->event)
- tasklet_kill(&substream->runtime->tasklet);
+ if (!up)
+ cancel_work_sync(&substream->runtime->event_work);
}
int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
@@ -182,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)
{
@@ -197,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;
@@ -211,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)
{
@@ -225,159 +219,145 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
spin_unlock_irqrestore(&runtime->lock, flags);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_drain_input);
-int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
- int mode, struct snd_rawmidi_file * rfile)
+/* look for an available substream for the given stream direction;
+ * if a specific subdevice is given, try to assign it
+ */
+static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
+ int stream, int mode,
+ struct snd_rawmidi_substream **sub_ret)
{
- struct snd_rawmidi *rmidi;
- struct list_head *list1, *list2;
- struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
- struct snd_rawmidi_runtime *input = NULL, *output = NULL;
- int err;
+ struct snd_rawmidi_substream *substream;
+ struct snd_rawmidi_str *s = &rmidi->streams[stream];
+ static unsigned int info_flags[2] = {
+ [SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
+ [SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
+ };
- if (rfile)
- rfile->input = rfile->output = NULL;
- mutex_lock(&register_mutex);
- rmidi = snd_rawmidi_search(card, device);
- mutex_unlock(&register_mutex);
- if (rmidi == NULL) {
- err = -ENODEV;
- goto __error1;
- }
- if (!try_module_get(rmidi->card->module)) {
- err = -EFAULT;
- goto __error1;
- }
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_lock(&rmidi->open_mutex);
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
- err = -ENXIO;
- goto __error;
- }
- if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
- err = -ENODEV;
- goto __error;
- }
- if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
- err = -EAGAIN;
- goto __error;
- }
- }
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
- err = -ENXIO;
- goto __error;
- }
- if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
- err = -ENODEV;
- goto __error;
+ if (!(rmidi->info_flags & info_flags[stream]))
+ return -ENXIO;
+ if (subdevice >= 0 && subdevice >= s->substream_count)
+ return -ENODEV;
+
+ list_for_each_entry(substream, &s->substreams, list) {
+ if (substream->opened) {
+ if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
+ !(mode & SNDRV_RAWMIDI_LFLG_APPEND) ||
+ !substream->append)
+ continue;
}
- if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
- err = -EAGAIN;
- goto __error;
+ if (subdevice < 0 || subdevice == substream->number) {
+ *sub_ret = substream;
+ return 0;
}
}
- list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
- while (1) {
- if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
- sinput = NULL;
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- err = -EAGAIN;
- goto __error;
- }
- break;
- }
- sinput = list_entry(list1, struct snd_rawmidi_substream, list);
- if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
- goto __nexti;
- if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
- break;
- __nexti:
- list1 = list1->next;
- }
- list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
- while (1) {
- if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
- soutput = NULL;
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- err = -EAGAIN;
- goto __error;
- }
- break;
- }
- soutput = list_entry(list2, struct snd_rawmidi_substream, list);
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
- if (soutput->opened && !soutput->append)
- goto __nexto;
- } else {
- if (soutput->opened)
- goto __nexto;
- }
+ return -EAGAIN;
+}
+
+/* open and do ref-counting for the given substream */
+static int open_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int mode)
+{
+ int err;
+
+ if (substream->use_count == 0) {
+ err = snd_rawmidi_runtime_create(substream);
+ if (err < 0)
+ return err;
+ err = substream->ops->open(substream);
+ if (err < 0) {
+ snd_rawmidi_runtime_free(substream);
+ return err;
}
- if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
- break;
- __nexto:
- list2 = list2->next;
+ substream->opened = 1;
+ substream->active_sensing = 0;
+ if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+ substream->append = 1;
+ substream->pid = get_pid(task_pid(current));
+ rmidi->streams[substream->stream].substream_opened++;
}
+ substream->use_count++;
+ return 0;
+}
+
+static void close_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int cleanup);
+
+static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
+ struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
+ int err;
+
+ rfile->input = rfile->output = NULL;
if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- if ((err = snd_rawmidi_runtime_create(sinput)) < 0)
- goto __error;
- input = sinput->runtime;
- if ((err = sinput->ops->open(sinput)) < 0)
- goto __error;
- sinput->opened = 1;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
- } else {
- sinput = NULL;
+ err = assign_substream(rmidi, subdevice,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ mode, &sinput);
+ if (err < 0)
+ return err;
}
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (soutput->opened)
- goto __skip_output;
- if ((err = snd_rawmidi_runtime_create(soutput)) < 0) {
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
- sinput->ops->close(sinput);
- goto __error;
- }
- output = soutput->runtime;
- if ((err = soutput->ops->open(soutput)) < 0) {
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
- sinput->ops->close(sinput);
- goto __error;
- }
- __skip_output:
- soutput->opened = 1;
- if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
- soutput->append = 1;
- if (soutput->use_count++ == 0)
- soutput->active_sensing = 1;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
- } else {
- soutput = NULL;
+ err = assign_substream(rmidi, subdevice,
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ mode, &soutput);
+ if (err < 0)
+ return err;
}
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_unlock(&rmidi->open_mutex);
- if (rfile) {
- rfile->rmidi = rmidi;
- rfile->input = sinput;
- rfile->output = soutput;
+
+ if (sinput) {
+ err = open_substream(rmidi, sinput, mode);
+ if (err < 0)
+ return err;
+ }
+ if (soutput) {
+ err = open_substream(rmidi, soutput, mode);
+ if (err < 0) {
+ if (sinput)
+ close_substream(rmidi, sinput, 0);
+ return err;
+ }
}
+
+ rfile->rmidi = rmidi;
+ rfile->input = sinput;
+ rfile->output = soutput;
return 0;
+}
- __error:
- if (input != NULL)
- snd_rawmidi_runtime_free(sinput);
- if (output != NULL)
- snd_rawmidi_runtime_free(soutput);
- module_put(rmidi->card->module);
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_unlock(&rmidi->open_mutex);
- __error1:
+/* called from sound/core/seq/seq_midi.c */
+int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
+ int mode, struct snd_rawmidi_file * rfile)
+{
+ struct snd_rawmidi *rmidi;
+ int err;
+
+ if (snd_BUG_ON(!rfile))
+ return -EINVAL;
+
+ mutex_lock(&register_mutex);
+ rmidi = snd_rawmidi_search(card, device);
+ if (rmidi == NULL) {
+ mutex_unlock(&register_mutex);
+ return -ENODEV;
+ }
+ if (!try_module_get(rmidi->card->module)) {
+ mutex_unlock(&register_mutex);
+ return -ENXIO;
+ }
+ mutex_unlock(&register_mutex);
+
+ mutex_lock(&rmidi->open_mutex);
+ err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
+ mutex_unlock(&rmidi->open_mutex);
+ if (err < 0)
+ module_put(rmidi->card->module);
return err;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_open);
static int snd_rawmidi_open(struct inode *inode, struct file *file)
{
@@ -387,11 +367,17 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
unsigned short fflags;
int err;
struct snd_rawmidi *rmidi;
- struct snd_rawmidi_file *rawmidi_file;
+ struct snd_rawmidi_file *rawmidi_file = NULL;
wait_queue_t wait;
- struct list_head *list;
struct snd_ctl_file *kctl;
+ if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+ return -EINVAL; /* invalid combination */
+
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
if (maj == snd_major) {
rmidi = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_RAWMIDI);
@@ -405,38 +391,39 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
if (rmidi == NULL)
return -ENODEV;
- if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
- return -EINVAL; /* invalid combination */
+
+ if (!try_module_get(rmidi->card->module)) {
+ snd_card_unref(rmidi->card);
+ return -ENXIO;
+ }
+
+ mutex_lock(&rmidi->open_mutex);
card = rmidi->card;
err = snd_card_file_add(card, file);
if (err < 0)
- return -ENODEV;
+ goto __error_card;
fflags = snd_rawmidi_file_flags(file);
if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
- fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
if (rawmidi_file == NULL) {
- snd_card_file_remove(card, file);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto __error;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&rmidi->open_wait, &wait);
- mutex_lock(&rmidi->open_mutex);
while (1) {
subdevice = -1;
- down_read(&card->controls_rwsem);
- list_for_each(list, &card->ctl_files) {
- kctl = snd_ctl_file(list);
- if (kctl->pid == current->pid) {
+ read_lock(&card->ctl_files_rwlock);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
+ if (kctl->pid == task_pid(current)) {
subdevice = kctl->prefer_rawmidi_subdevice;
if (subdevice != -1)
break;
}
}
- up_read(&card->controls_rwsem);
- err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,
- subdevice, fflags, rawmidi_file);
+ read_unlock(&card->ctl_files_rwlock);
+ err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
if (err >= 0)
break;
if (err == -EAGAIN) {
@@ -450,89 +437,121 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
mutex_unlock(&rmidi->open_mutex);
schedule();
mutex_lock(&rmidi->open_mutex);
+ if (rmidi->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
}
+ remove_wait_queue(&rmidi->open_wait, &wait);
+ if (err < 0) {
+ kfree(rawmidi_file);
+ goto __error;
+ }
#ifdef CONFIG_SND_OSSEMUL
if (rawmidi_file->input && rawmidi_file->input->runtime)
rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
if (rawmidi_file->output && rawmidi_file->output->runtime)
rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
#endif
- remove_wait_queue(&rmidi->open_wait, &wait);
- if (err >= 0) {
- file->private_data = rawmidi_file;
- } else {
- snd_card_file_remove(card, file);
- kfree(rawmidi_file);
- }
+ file->private_data = rawmidi_file;
mutex_unlock(&rmidi->open_mutex);
+ snd_card_unref(rmidi->card);
+ return 0;
+
+ __error:
+ snd_card_file_remove(card, file);
+ __error_card:
+ mutex_unlock(&rmidi->open_mutex);
+ module_put(rmidi->card->module);
+ snd_card_unref(rmidi->card);
return err;
}
-int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)
+static void close_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int cleanup)
{
- struct snd_rawmidi *rmidi;
- struct snd_rawmidi_substream *substream;
- struct snd_rawmidi_runtime *runtime;
+ if (--substream->use_count)
+ return;
- snd_assert(rfile != NULL, return -ENXIO);
- snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
- rmidi = rfile->rmidi;
- mutex_lock(&rmidi->open_mutex);
- if (rfile->input != NULL) {
- substream = rfile->input;
- rfile->input = NULL;
- runtime = substream->runtime;
- snd_rawmidi_input_trigger(substream, 0);
- substream->ops->close(substream);
- if (runtime->private_free != NULL)
- runtime->private_free(substream);
- snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
- }
- if (rfile->output != NULL) {
- substream = rfile->output;
- rfile->output = NULL;
- if (--substream->use_count == 0) {
- runtime = substream->runtime;
+ if (cleanup) {
+ if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+ snd_rawmidi_input_trigger(substream, 0);
+ else {
if (substream->active_sensing) {
unsigned char buf = 0xfe;
- /* sending single active sensing message to shut the device up */
+ /* sending single active sensing message
+ * to shut the device up
+ */
snd_rawmidi_kernel_write(substream, &buf, 1);
}
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0);
- substream->ops->close(substream);
- if (runtime->private_free != NULL)
- runtime->private_free(substream);
- snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- substream->append = 0;
}
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
}
+ substream->ops->close(substream);
+ if (substream->runtime->private_free)
+ substream->runtime->private_free(substream);
+ snd_rawmidi_runtime_free(substream);
+ substream->opened = 0;
+ substream->append = 0;
+ put_pid(substream->pid);
+ substream->pid = NULL;
+ rmidi->streams[substream->stream].substream_opened--;
+}
+
+static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi *rmidi;
+
+ rmidi = rfile->rmidi;
+ mutex_lock(&rmidi->open_mutex);
+ if (rfile->input) {
+ close_substream(rmidi, rfile->input, 1);
+ rfile->input = NULL;
+ }
+ if (rfile->output) {
+ close_substream(rmidi, rfile->output, 1);
+ rfile->output = NULL;
+ }
+ rfile->rmidi = NULL;
mutex_unlock(&rmidi->open_mutex);
+ wake_up(&rmidi->open_wait);
+}
+
+/* called from sound/core/seq/seq_midi.c */
+int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi *rmidi;
+
+ if (snd_BUG_ON(!rfile))
+ return -ENXIO;
+
+ rmidi = rfile->rmidi;
+ rawmidi_release_priv(rfile);
module_put(rmidi->card->module);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_release);
static int snd_rawmidi_release(struct inode *inode, struct file *file)
{
struct snd_rawmidi_file *rfile;
struct snd_rawmidi *rmidi;
- int err;
+ struct module *module;
rfile = file->private_data;
- err = snd_rawmidi_kernel_release(rfile);
rmidi = rfile->rmidi;
- wake_up(&rmidi->open_wait);
+ rawmidi_release_priv(rfile);
kfree(rfile);
+ module = rmidi->card->module;
snd_card_file_remove(rmidi->card, file);
- return err;
+ module_put(module);
+ return 0;
}
static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
@@ -575,7 +594,6 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *pstr;
struct snd_rawmidi_substream *substream;
- struct list_head *list;
mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, info->device);
@@ -589,13 +607,13 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
return -ENOENT;
if (info->subdevice >= pstr->substream_count)
return -ENXIO;
- list_for_each(list, &pstr->substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &pstr->substreams, list) {
if ((unsigned int)substream->number == info->subdevice)
return snd_rawmidi_info(substream, 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)
@@ -631,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;
@@ -643,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)
@@ -658,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)
@@ -799,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;
}
@@ -821,6 +840,8 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)argp))
return -EFAULT;
+ if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
+ device = SNDRV_RAWMIDI_DEVICES - 1;
mutex_lock(&register_mutex);
device = device < 0 ? 0 : device + 1;
while (device < SNDRV_RAWMIDI_DEVICES) {
@@ -858,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)
@@ -867,8 +888,11 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
int result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ if (!substream->opened)
+ return -EBADFD;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_receive: input is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_receive: input is not active!!!\n");
return -EINVAL;
}
spin_lock_irqsave(&runtime->lock, flags);
@@ -912,16 +936,18 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
}
if (result > 0) {
if (runtime->event)
- tasklet_hi_schedule(&runtime->tasklet);
+ schedule_work(&runtime->event_work);
else if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
+EXPORT_SYMBOL(snd_rawmidi_receive);
static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
- unsigned char *buf, long count, int kernel)
+ unsigned char __user *userbuf,
+ unsigned char *kernelbuf, long count)
{
unsigned long flags;
long result = 0, count1;
@@ -934,11 +960,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
spin_lock_irqsave(&runtime->lock, flags);
if (count1 > (int)runtime->avail)
count1 = runtime->avail;
- if (kernel) {
- memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
- } else {
+ if (kernelbuf)
+ memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+ if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
- if (copy_to_user((char __user *)buf + result,
+ if (copy_to_user(userbuf + result,
runtime->buffer + runtime->appl_ptr, count1)) {
return result > 0 ? result : -EFAULT;
}
@@ -958,8 +984,9 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
unsigned char *buf, long count)
{
snd_rawmidi_input_trigger(substream, 1);
- return snd_rawmidi_kernel_read1(substream, buf, count, 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)
@@ -991,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)
@@ -999,8 +1028,9 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
}
spin_unlock_irq(&runtime->lock);
count1 = snd_rawmidi_kernel_read1(substream,
- (unsigned char __force *)buf,
- count, 0);
+ (unsigned char __user *)buf,
+ NULL/*kernelbuf*/,
+ count);
if (count1 < 0)
return result > 0 ? result : count1;
result += count1;
@@ -1013,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)
{
@@ -1023,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);
@@ -1031,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
@@ -1044,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)
@@ -1054,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;
@@ -1086,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)
{
@@ -1104,11 +1138,12 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_ack: output is not active!!!\n");
return -EINVAL;
}
spin_lock_irqsave(&runtime->lock, flags);
- snd_assert(runtime->avail + count <= runtime->buffer_size, );
+ snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count;
@@ -1120,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
@@ -1129,26 +1165,33 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
*
* Copies data from the buffer to the device and advances the pointer.
*
- * Returns the copied size if successful, or a negative error code on failure.
+ * Return: The copied size if successful, or a negative error code on failure.
*/
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
+ if (!substream->opened)
+ return -EBADFD;
count = snd_rawmidi_transmit_peek(substream, buffer, count);
if (count < 0)
return count;
return snd_rawmidi_transmit_ack(substream, count);
}
+EXPORT_SYMBOL(snd_rawmidi_transmit);
static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
- const unsigned char *buf, long count, int kernel)
+ const unsigned char __user *userbuf,
+ const unsigned char *kernelbuf,
+ long count)
{
unsigned long flags;
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
- snd_assert(buf != NULL, return -EINVAL);
- snd_assert(runtime->buffer != NULL, return -EINVAL);
+ if (snd_BUG_ON(!kernelbuf && !userbuf))
+ return -EINVAL;
+ if (snd_BUG_ON(!runtime->buffer))
+ return -EINVAL;
result = 0;
spin_lock_irqsave(&runtime->lock, flags);
@@ -1164,12 +1207,13 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (long)runtime->avail)
count1 = runtime->avail;
- if (kernel) {
- memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
- } else {
+ if (kernelbuf)
+ memcpy(runtime->buffer + runtime->appl_ptr,
+ kernelbuf + result, count1);
+ else if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
if (copy_from_user(runtime->buffer + runtime->appl_ptr,
- (char __user *)buf, count1)) {
+ userbuf + result, count1)) {
spin_lock_irqsave(&runtime->lock, flags);
result = result > 0 ? result : -EFAULT;
goto __end;
@@ -1180,7 +1224,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
runtime->appl_ptr %= runtime->buffer_size;
runtime->avail -= count1;
result += count1;
- buf += count1;
count -= count1;
}
__end:
@@ -1194,8 +1237,9 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
const unsigned char *buf, long count)
{
- return snd_rawmidi_kernel_write1(substream, buf, count, 1);
+ 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)
@@ -1227,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)
@@ -1234,9 +1280,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
spin_lock_irq(&runtime->lock);
}
spin_unlock_irq(&runtime->lock);
- count1 = snd_rawmidi_kernel_write1(substream,
- (unsigned char __force *)buf,
- count, 0);
+ count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
if (count1 < 0)
return result > 0 ? result : count1;
result += count1;
@@ -1245,7 +1289,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
break;
count -= count1;
}
- if (file->f_flags & O_SYNC) {
+ if (file->f_flags & O_DSYNC) {
spin_lock_irq(&runtime->lock);
while (runtime->avail != runtime->buffer_size) {
wait_queue_t wait;
@@ -1313,20 +1357,23 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_runtime *runtime;
- struct list_head *list;
rmidi = entry->private_data;
snd_iprintf(buffer, "%s\n\n", rmidi->name);
mutex_lock(&rmidi->open_mutex);
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
- list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+ list) {
snd_iprintf(buffer,
"Output %d\n"
" Tx bytes : %lu\n",
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Mode : %s\n"
@@ -1339,14 +1386,18 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
}
}
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
- list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
+ list) {
snd_iprintf(buffer,
"Input %d\n"
" Rx bytes : %lu\n",
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Buffer size : %lu\n"
@@ -1365,13 +1416,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
* Register functions
*/
-static struct file_operations snd_rawmidi_f_ops =
+static const struct file_operations snd_rawmidi_f_ops =
{
.owner = THIS_MODULE,
.read = snd_rawmidi_read,
.write = snd_rawmidi_write,
.open = snd_rawmidi_open,
.release = snd_rawmidi_release,
+ .llseek = no_llseek,
.poll = snd_rawmidi_poll,
.unlocked_ioctl = snd_rawmidi_ioctl,
.compat_ioctl = snd_rawmidi_ioctl_compat,
@@ -1385,11 +1437,10 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
struct snd_rawmidi_substream *substream;
int idx;
- INIT_LIST_HEAD(&stream->substreams);
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;
@@ -1414,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,
@@ -1428,18 +1479,22 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
.dev_disconnect = snd_rawmidi_dev_disconnect,
};
- snd_assert(rrawmidi != NULL, return -EINVAL);
- *rrawmidi = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rrawmidi)
+ *rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
if (rmidi == NULL) {
- snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
+ dev_err(card->dev, "rawmidi: cannot allocate\n");
return -ENOMEM;
}
rmidi->card = card;
rmidi->device = device;
mutex_init(&rmidi->open_mutex);
init_waitqueue_head(&rmidi->open_wait);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
+
if (id != NULL)
strlcpy(rmidi->id, id, sizeof(rmidi->id));
if ((err = snd_rawmidi_alloc_substreams(rmidi,
@@ -1460,9 +1515,11 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
snd_rawmidi_free(rmidi);
return err;
}
- *rrawmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = rmidi;
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_new);
static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
{
@@ -1477,7 +1534,8 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{
- snd_assert(rmidi != NULL, return -ENXIO);
+ if (!rmidi)
+ return 0;
snd_info_free_entry(rmidi->proc_entry);
rmidi->proc_entry = NULL;
@@ -1527,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(&register_mutex);
return err;
@@ -1544,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
@@ -1556,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++;
}
@@ -1591,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(&register_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]) {
@@ -1608,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(&register_mutex);
return 0;
}
@@ -1623,14 +1698,12 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
struct snd_rawmidi_ops *ops)
{
- struct list_head *list;
struct snd_rawmidi_substream *substream;
- list_for_each(list, &rmidi->streams[stream].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
substream->ops = ops;
- }
}
+EXPORT_SYMBOL(snd_rawmidi_set_ops);
/*
* ENTRY functions
@@ -1646,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;
}
}
@@ -1667,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 9f7b32e1ccd..f3420d11a12 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -20,14 +20,14 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/log2.h>
#include <sound/core.h>
#include <sound/timer.h>
-#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
+#if IS_ENABLED(CONFIG_RTC)
#include <linux/mc146818rtc.h>
@@ -91,7 +91,8 @@ static int
rtctimer_start(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
- snd_assert(rtc != NULL, return -EINVAL);
+ if (snd_BUG_ON(!rtc))
+ return -EINVAL;
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
rtc_control(rtc, RTC_PIE_ON, 0);
return 0;
@@ -101,7 +102,8 @@ static int
rtctimer_stop(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
- snd_assert(rtc != NULL, return -EINVAL);
+ if (snd_BUG_ON(!rtc))
+ return -EINVAL;
rtc_control(rtc, RTC_PIE_OFF, 0);
return 0;
}
@@ -116,7 +118,7 @@ static void rtctimer_tasklet(unsigned long data)
*/
static void rtctimer_interrupt(void *private_data)
{
- tasklet_hi_schedule(private_data);
+ tasklet_schedule(private_data);
}
@@ -129,9 +131,8 @@ static int __init rtctimer_init(void)
struct snd_timer *timer;
if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
- (rtctimer_freq & (rtctimer_freq - 1)) != 0) {
- snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n",
- rtctimer_freq);
+ !is_power_of_2(rtctimer_freq)) {
+ pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq);
return -EINVAL;
}
@@ -183,4 +184,4 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
-#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
+#endif /* IS_ENABLED(CONFIG_RTC) */
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
new file mode 100644
index 00000000000..b851fd890a8
--- /dev/null
+++ b/sound/core/seq/Kconfig
@@ -0,0 +1,16 @@
+# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
+
+config SND_RAWMIDI_SEQ
+ def_tristate SND_SEQUENCER && SND_RAWMIDI
+
+config SND_OPL3_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL3_LIB
+
+config SND_OPL4_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL4_LIB
+
+config SND_SBAWE_SEQ
+ def_tristate SND_SEQUENCER && SND_SBAWE
+
+config SND_EMU10K1_SEQ
+ def_tristate SND_SEQUENCER && SND_EMU10K1
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 402e2b4a34c..941f64a853e 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -1,13 +1,8 @@
#
# Makefile for ALSA
-# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-obj-$(CONFIG_SND) += instr/
-ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
- obj-$(CONFIG_SND_SEQUENCER) += oss/
-endif
-
snd-seq-device-objs := seq_device.o
snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
@@ -15,30 +10,20 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o
-snd-seq-instr-objs := seq_instr.o
snd-seq-dummy-objs := seq_dummy.o
snd-seq-virmidi-objs := seq_virmidi.o
-#
-# this function returns:
-# "m" - CONFIG_SND_SEQUENCER is m
-# <empty string> - CONFIG_SND_SEQUENCER is undefined
-# otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
-obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
+ obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
+ obj-$(CONFIG_SND_SEQUENCER) += oss/
endif
obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
deleted file mode 100644
index 69138f30a29..00000000000
--- a/sound/core/seq/instr/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Makefile for ALSA
-# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
-#
-
-snd-ainstr-fm-objs := ainstr_fm.o
-snd-ainstr-simple-objs := ainstr_simple.o
-snd-ainstr-gf1-objs := ainstr_gf1.o
-snd-ainstr-iw-objs := ainstr_iw.o
-
-#
-# this function returns:
-# "m" - CONFIG_SND_SEQUENCER is m
-# <empty string> - CONFIG_SND_SEQUENCER is undefined
-# otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
-# Toplevel Module Dependency
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c
deleted file mode 100644
index b09babf272c..00000000000
--- a/sound/core/seq/instr/ainstr_fm.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * FM (OPL2/3) Instrument routines
- * Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <sound/core.h>
-#include <sound/ainstr_fm.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
-MODULE_LICENSE("GPL");
-
-static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic, int cmd)
-{
- struct fm_instrument *ip;
- struct fm_xinstrument ix;
- int idx;
-
- if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
- return -EINVAL;
- /* copy instrument data */
- if (len < (long)sizeof(ix))
- return -EINVAL;
- if (copy_from_user(&ix, instr_data, sizeof(ix)))
- return -EFAULT;
- if (ix.stype != FM_STRU_INSTR)
- return -EINVAL;
- ip = (struct fm_instrument *)KINSTR_DATA(instr);
- ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
- ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
- ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
- ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
- ip->type = ix.type;
- for (idx = 0; idx < 4; idx++) {
- ip->op[idx].am_vib = ix.op[idx].am_vib;
- ip->op[idx].ksl_level = ix.op[idx].ksl_level;
- ip->op[idx].attack_decay = ix.op[idx].attack_decay;
- ip->op[idx].sustain_release = ix.op[idx].sustain_release;
- ip->op[idx].wave_select = ix.op[idx].wave_select;
- }
- for (idx = 0; idx < 2; idx++) {
- ip->feedback_connection[idx] = ix.feedback_connection[idx];
- }
- ip->echo_delay = ix.echo_delay;
- ip->echo_atten = ix.echo_atten;
- ip->chorus_spread = ix.chorus_spread;
- ip->trnsps = ix.trnsps;
- ip->fix_dur = ix.fix_dur;
- ip->modes = ix.modes;
- ip->fix_key = ix.fix_key;
- return 0;
-}
-
-static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic,
- int cmd)
-{
- struct fm_instrument *ip;
- struct fm_xinstrument ix;
- int idx;
-
- if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
- return -EINVAL;
- if (len < (long)sizeof(ix))
- return -ENOMEM;
- memset(&ix, 0, sizeof(ix));
- ip = (struct fm_instrument *)KINSTR_DATA(instr);
- ix.stype = FM_STRU_INSTR;
- ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
- ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
- ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
- ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
- ix.type = ip->type;
- for (idx = 0; idx < 4; idx++) {
- ix.op[idx].am_vib = ip->op[idx].am_vib;
- ix.op[idx].ksl_level = ip->op[idx].ksl_level;
- ix.op[idx].attack_decay = ip->op[idx].attack_decay;
- ix.op[idx].sustain_release = ip->op[idx].sustain_release;
- ix.op[idx].wave_select = ip->op[idx].wave_select;
- }
- for (idx = 0; idx < 2; idx++) {
- ix.feedback_connection[idx] = ip->feedback_connection[idx];
- }
- if (copy_to_user(instr_data, &ix, sizeof(ix)))
- return -EFAULT;
- ix.echo_delay = ip->echo_delay;
- ix.echo_atten = ip->echo_atten;
- ix.chorus_spread = ip->chorus_spread;
- ix.trnsps = ip->trnsps;
- ix.fix_dur = ip->fix_dur;
- ix.modes = ip->modes;
- ix.fix_key = ip->fix_key;
- return 0;
-}
-
-static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr,
- long *size)
-{
- *size = sizeof(struct fm_xinstrument);
- return 0;
-}
-
-int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_ops *next)
-{
- memset(ops, 0, sizeof(*ops));
- // ops->private_data = private_data;
- ops->add_len = sizeof(struct fm_instrument);
- ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
- ops->put = snd_seq_fm_put;
- ops->get = snd_seq_fm_get;
- ops->get_size = snd_seq_fm_get_size;
- // ops->remove = snd_seq_fm_remove;
- // ops->notify = snd_seq_fm_notify;
- ops->next = next;
- return 0;
-}
-
-/*
- * Init part
- */
-
-static int __init alsa_ainstr_fm_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ainstr_fm_exit(void)
-{
-}
-
-module_init(alsa_ainstr_fm_init)
-module_exit(alsa_ainstr_fm_exit)
-
-EXPORT_SYMBOL(snd_seq_fm_init);
diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
deleted file mode 100644
index 3c31038a74b..00000000000
--- a/sound/core/seq/instr/ainstr_gf1.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * GF1 (GUS) Patch - Instrument routines
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_gf1.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
-{
- unsigned int result = size;
-
- if (format & GF1_WAVE_16BIT)
- result <<= 1;
- if (format & GF1_WAVE_STEREO)
- result <<= 1;
- return format;
-}
-
-static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops,
- struct gf1_instrument *ip,
- char __user **data,
- long *len,
- int atomic)
-{
- struct gf1_wave *wp, *prev;
- struct gf1_xwave xp;
- int err;
- gfp_t gfp_mask;
- unsigned int real_size;
-
- gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
- if (*len < (long)sizeof(xp))
- return -EINVAL;
- if (copy_from_user(&xp, *data, sizeof(xp)))
- return -EFAULT;
- *data += sizeof(xp);
- *len -= sizeof(xp);
- wp = kzalloc(sizeof(*wp), gfp_mask);
- if (wp == NULL)
- return -ENOMEM;
- wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
- wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
- wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
- wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
- wp->format = le32_to_cpu(xp.format);
- wp->size = le32_to_cpu(xp.size);
- wp->start = le32_to_cpu(xp.start);
- wp->loop_start = le32_to_cpu(xp.loop_start);
- wp->loop_end = le32_to_cpu(xp.loop_end);
- wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
- wp->flags = xp.flags;
- wp->sample_rate = le32_to_cpu(xp.sample_rate);
- wp->low_frequency = le32_to_cpu(xp.low_frequency);
- wp->high_frequency = le32_to_cpu(xp.high_frequency);
- wp->root_frequency = le32_to_cpu(xp.root_frequency);
- wp->tune = le16_to_cpu(xp.tune);
- wp->balance = xp.balance;
- memcpy(wp->envelope_rate, xp.envelope_rate, 6);
- memcpy(wp->envelope_offset, xp.envelope_offset, 6);
- wp->tremolo_sweep = xp.tremolo_sweep;
- wp->tremolo_rate = xp.tremolo_rate;
- wp->tremolo_depth = xp.tremolo_depth;
- wp->vibrato_sweep = xp.vibrato_sweep;
- wp->vibrato_rate = xp.vibrato_rate;
- wp->vibrato_depth = xp.vibrato_depth;
- wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
- wp->scale_factor = le16_to_cpu(xp.scale_factor);
- real_size = snd_seq_gf1_size(wp->size, wp->format);
- if ((long)real_size > *len) {
- kfree(wp);
- return -ENOMEM;
- }
- if (ops->put_sample) {
- err = ops->put_sample(ops->private_data, wp,
- *data, real_size, atomic);
- if (err < 0) {
- kfree(wp);
- return err;
- }
- }
- *data += real_size;
- *len -= real_size;
- prev = ip->wave;
- if (prev) {
- while (prev->next) prev = prev->next;
- prev->next = wp;
- } else {
- ip->wave = wp;
- }
- return 0;
-}
-
-static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops,
- struct gf1_wave *wave,
- int atomic)
-{
- if (ops->remove_sample)
- ops->remove_sample(ops->private_data, wave, atomic);
- kfree(wave);
-}
-
-static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops,
- struct gf1_instrument *ip,
- int atomic)
-{
- struct gf1_wave *wave;
-
- while ((wave = ip->wave) != NULL) {
- ip->wave = wave->next;
- snd_seq_gf1_wave_free(ops, wave, atomic);
- }
-}
-
-static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic,
- int cmd)
-{
- struct snd_gf1_ops *ops = private_data;
- struct gf1_instrument *ip;
- struct gf1_xinstrument ix;
- int err;
- gfp_t gfp_mask;
-
- if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
- return -EINVAL;
- gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
- /* copy instrument data */
- if (len < (long)sizeof(ix))
- return -EINVAL;
- if (copy_from_user(&ix, instr_data, sizeof(ix)))
- return -EFAULT;
- if (ix.stype != GF1_STRU_INSTR)
- return -EINVAL;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- ip = (struct gf1_instrument *)KINSTR_DATA(instr);
- ip->exclusion = le16_to_cpu(ix.exclusion);
- ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
- ip->effect1 = ix.effect1;
- ip->effect1_depth = ix.effect1_depth;
- ip->effect2 = ix.effect2;
- ip->effect2_depth = ix.effect2_depth;
- /* copy layers */
- while (len > (long)sizeof(__u32)) {
- __u32 stype;
-
- if (copy_from_user(&stype, instr_data, sizeof(stype)))
- return -EFAULT;
- if (stype != GF1_STRU_WAVE) {
- snd_seq_gf1_instr_free(ops, ip, atomic);
- return -EINVAL;
- }
- err = snd_seq_gf1_copy_wave_from_stream(ops,
- ip,
- &instr_data,
- &len,
- atomic);
- if (err < 0) {
- snd_seq_gf1_instr_free(ops, ip, atomic);
- return err;
- }
- }
- return 0;
-}
-
-static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops,
- struct gf1_instrument *ip,
- char __user **data,
- long *len,
- int atomic)
-{
- struct gf1_wave *wp;
- struct gf1_xwave xp;
- int err;
- unsigned int real_size;
-
- for (wp = ip->wave; wp; wp = wp->next) {
- if (*len < (long)sizeof(xp))
- return -ENOMEM;
- memset(&xp, 0, sizeof(xp));
- xp.stype = GF1_STRU_WAVE;
- xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
- xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
- xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
- xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
- xp.format = cpu_to_le32(wp->format);
- xp.size = cpu_to_le32(wp->size);
- xp.start = cpu_to_le32(wp->start);
- xp.loop_start = cpu_to_le32(wp->loop_start);
- xp.loop_end = cpu_to_le32(wp->loop_end);
- xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
- xp.flags = wp->flags;
- xp.sample_rate = cpu_to_le32(wp->sample_rate);
- xp.low_frequency = cpu_to_le32(wp->low_frequency);
- xp.high_frequency = cpu_to_le32(wp->high_frequency);
- xp.root_frequency = cpu_to_le32(wp->root_frequency);
- xp.tune = cpu_to_le16(wp->tune);
- xp.balance = wp->balance;
- memcpy(xp.envelope_rate, wp->envelope_rate, 6);
- memcpy(xp.envelope_offset, wp->envelope_offset, 6);
- xp.tremolo_sweep = wp->tremolo_sweep;
- xp.tremolo_rate = wp->tremolo_rate;
- xp.tremolo_depth = wp->tremolo_depth;
- xp.vibrato_sweep = wp->vibrato_sweep;
- xp.vibrato_rate = wp->vibrato_rate;
- xp.vibrato_depth = wp->vibrato_depth;
- xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
- xp.scale_factor = cpu_to_le16(wp->scale_factor);
- if (copy_to_user(*data, &xp, sizeof(xp)))
- return -EFAULT;
- *data += sizeof(xp);
- *len -= sizeof(xp);
- real_size = snd_seq_gf1_size(wp->size, wp->format);
- if (*len < (long)real_size)
- return -ENOMEM;
- if (ops->get_sample) {
- err = ops->get_sample(ops->private_data, wp,
- *data, real_size, atomic);
- if (err < 0)
- return err;
- }
- *data += wp->size;
- *len -= wp->size;
- }
- return 0;
-}
-
-static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic,
- int cmd)
-{
- struct snd_gf1_ops *ops = private_data;
- struct gf1_instrument *ip;
- struct gf1_xinstrument ix;
-
- if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
- return -EINVAL;
- if (len < (long)sizeof(ix))
- return -ENOMEM;
- memset(&ix, 0, sizeof(ix));
- ip = (struct gf1_instrument *)KINSTR_DATA(instr);
- ix.stype = GF1_STRU_INSTR;
- ix.exclusion = cpu_to_le16(ip->exclusion);
- ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
- ix.effect1 = cpu_to_le16(ip->effect1);
- ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
- ix.effect2 = ip->effect2;
- ix.effect2_depth = ip->effect2_depth;
- if (copy_to_user(instr_data, &ix, sizeof(ix)))
- return -EFAULT;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- return snd_seq_gf1_copy_wave_to_stream(ops,
- ip,
- &instr_data,
- &len,
- atomic);
-}
-
-static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr,
- long *size)
-{
- long result;
- struct gf1_instrument *ip;
- struct gf1_wave *wp;
-
- *size = 0;
- ip = (struct gf1_instrument *)KINSTR_DATA(instr);
- result = sizeof(struct gf1_xinstrument);
- for (wp = ip->wave; wp; wp = wp->next) {
- result += sizeof(struct gf1_xwave);
- result += wp->size;
- }
- *size = result;
- return 0;
-}
-
-static int snd_seq_gf1_remove(void *private_data,
- struct snd_seq_kinstr *instr,
- int atomic)
-{
- struct snd_gf1_ops *ops = private_data;
- struct gf1_instrument *ip;
-
- ip = (struct gf1_instrument *)KINSTR_DATA(instr);
- snd_seq_gf1_instr_free(ops, ip, atomic);
- return 0;
-}
-
-static void snd_seq_gf1_notify(void *private_data,
- struct snd_seq_kinstr *instr,
- int what)
-{
- struct snd_gf1_ops *ops = private_data;
-
- if (ops->notify)
- ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_gf1_init(struct snd_gf1_ops *ops,
- void *private_data,
- struct snd_seq_kinstr_ops *next)
-{
- memset(ops, 0, sizeof(*ops));
- ops->private_data = private_data;
- ops->kops.private_data = ops;
- ops->kops.add_len = sizeof(struct gf1_instrument);
- ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
- ops->kops.put = snd_seq_gf1_put;
- ops->kops.get = snd_seq_gf1_get;
- ops->kops.get_size = snd_seq_gf1_get_size;
- ops->kops.remove = snd_seq_gf1_remove;
- ops->kops.notify = snd_seq_gf1_notify;
- ops->kops.next = next;
- return 0;
-}
-
-/*
- * Init part
- */
-
-static int __init alsa_ainstr_gf1_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ainstr_gf1_exit(void)
-{
-}
-
-module_init(alsa_ainstr_gf1_init)
-module_exit(alsa_ainstr_gf1_exit)
-
-EXPORT_SYMBOL(snd_seq_gf1_init);
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
deleted file mode 100644
index 7f8df19ba56..00000000000
--- a/sound/core/seq/instr/ainstr_iw.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * IWFFFF - AMD InterWave (tm) - Instrument routines
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_iw.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
-{
- unsigned int result = size;
-
- if (format & IWFFFF_WAVE_16BIT)
- result <<= 1;
- if (format & IWFFFF_WAVE_STEREO)
- result <<= 1;
- return result;
-}
-
-static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp,
- struct iwffff_xlfo *fx)
-{
- fp->freq = le16_to_cpu(fx->freq);
- fp->depth = le16_to_cpu(fx->depth);
- fp->sweep = le16_to_cpu(fx->sweep);
- fp->shape = fx->shape;
- fp->delay = fx->delay;
-}
-
-static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
- struct iwffff_layer *lp,
- struct iwffff_env *ep,
- struct iwffff_xenv *ex,
- char __user **data,
- long *len,
- gfp_t gfp_mask)
-{
- __u32 stype;
- struct iwffff_env_record *rp, *rp_last;
- struct iwffff_xenv_record rx;
- struct iwffff_env_point *pp;
- struct iwffff_xenv_point px;
- int points_size, idx;
-
- ep->flags = ex->flags;
- ep->mode = ex->mode;
- ep->index = ex->index;
- rp_last = NULL;
- while (1) {
- if (*len < (long)sizeof(__u32))
- return -EINVAL;
- if (copy_from_user(&stype, *data, sizeof(stype)))
- return -EFAULT;
- if (stype == IWFFFF_STRU_WAVE)
- return 0;
- if (req_stype != stype) {
- if (stype == IWFFFF_STRU_ENV_RECP ||
- stype == IWFFFF_STRU_ENV_RECV)
- return 0;
- }
- if (*len < (long)sizeof(rx))
- return -EINVAL;
- if (copy_from_user(&rx, *data, sizeof(rx)))
- return -EFAULT;
- *data += sizeof(rx);
- *len -= sizeof(rx);
- points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
- if (points_size > *len)
- return -EINVAL;
- rp = kzalloc(sizeof(*rp) + points_size, gfp_mask);
- if (rp == NULL)
- return -ENOMEM;
- rp->nattack = le16_to_cpu(rx.nattack);
- rp->nrelease = le16_to_cpu(rx.nrelease);
- rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
- rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
- rp->release_rate = le16_to_cpu(rx.release_rate);
- rp->hirange = rx.hirange;
- pp = (struct iwffff_env_point *)(rp + 1);
- for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
- if (copy_from_user(&px, *data, sizeof(px)))
- return -EFAULT;
- *data += sizeof(px);
- *len -= sizeof(px);
- pp->offset = le16_to_cpu(px.offset);
- pp->rate = le16_to_cpu(px.rate);
- }
- if (ep->record == NULL) {
- ep->record = rp;
- } else {
- rp_last = rp;
- }
- rp_last = rp;
- }
- return 0;
-}
-
-static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops,
- struct iwffff_layer *lp,
- char __user **data,
- long *len,
- int atomic)
-{
- struct iwffff_wave *wp, *prev;
- struct iwffff_xwave xp;
- int err;
- gfp_t gfp_mask;
- unsigned int real_size;
-
- gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
- if (*len < (long)sizeof(xp))
- return -EINVAL;
- if (copy_from_user(&xp, *data, sizeof(xp)))
- return -EFAULT;
- *data += sizeof(xp);
- *len -= sizeof(xp);
- wp = kzalloc(sizeof(*wp), gfp_mask);
- if (wp == NULL)
- return -ENOMEM;
- wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
- wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
- wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
- wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
- wp->format = le32_to_cpu(xp.format);
- wp->address.memory = le32_to_cpu(xp.offset);
- wp->size = le32_to_cpu(xp.size);
- wp->start = le32_to_cpu(xp.start);
- wp->loop_start = le32_to_cpu(xp.loop_start);
- wp->loop_end = le32_to_cpu(xp.loop_end);
- wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
- wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
- wp->attenuation = xp.attenuation;
- wp->low_note = xp.low_note;
- wp->high_note = xp.high_note;
- real_size = snd_seq_iwffff_size(wp->size, wp->format);
- if (!(wp->format & IWFFFF_WAVE_ROM)) {
- if ((long)real_size > *len) {
- kfree(wp);
- return -ENOMEM;
- }
- }
- if (ops->put_sample) {
- err = ops->put_sample(ops->private_data, wp,
- *data, real_size, atomic);
- if (err < 0) {
- kfree(wp);
- return err;
- }
- }
- if (!(wp->format & IWFFFF_WAVE_ROM)) {
- *data += real_size;
- *len -= real_size;
- }
- prev = lp->wave;
- if (prev) {
- while (prev->next) prev = prev->next;
- prev->next = wp;
- } else {
- lp->wave = wp;
- }
- return 0;
-}
-
-static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops,
- struct iwffff_env *env,
- int atomic)
-{
- struct iwffff_env_record *rec;
-
- while ((rec = env->record) != NULL) {
- env->record = rec->next;
- kfree(rec);
- }
-}
-
-static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops,
- struct iwffff_wave *wave,
- int atomic)
-{
- if (ops->remove_sample)
- ops->remove_sample(ops->private_data, wave, atomic);
- kfree(wave);
-}
-
-static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops,
- struct iwffff_instrument *ip,
- int atomic)
-{
- struct iwffff_layer *layer;
- struct iwffff_wave *wave;
-
- while ((layer = ip->layer) != NULL) {
- ip->layer = layer->next;
- snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
- snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
- while ((wave = layer->wave) != NULL) {
- layer->wave = wave->next;
- snd_seq_iwffff_wave_free(ops, wave, atomic);
- }
- kfree(layer);
- }
-}
-
-static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic,
- int cmd)
-{
- struct snd_iwffff_ops *ops = private_data;
- struct iwffff_instrument *ip;
- struct iwffff_xinstrument ix;
- struct iwffff_layer *lp, *prev_lp;
- struct iwffff_xlayer lx;
- int err;
- gfp_t gfp_mask;
-
- if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
- return -EINVAL;
- gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
- /* copy instrument data */
- if (len < (long)sizeof(ix))
- return -EINVAL;
- if (copy_from_user(&ix, instr_data, sizeof(ix)))
- return -EFAULT;
- if (ix.stype != IWFFFF_STRU_INSTR)
- return -EINVAL;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
- ip->exclusion = le16_to_cpu(ix.exclusion);
- ip->layer_type = le16_to_cpu(ix.layer_type);
- ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
- ip->effect1 = ix.effect1;
- ip->effect1_depth = ix.effect1_depth;
- ip->effect2 = ix.effect2;
- ip->effect2_depth = ix.effect2_depth;
- /* copy layers */
- prev_lp = NULL;
- while (len > 0) {
- if (len < (long)sizeof(struct iwffff_xlayer)) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return -EINVAL;
- }
- if (copy_from_user(&lx, instr_data, sizeof(lx)))
- return -EFAULT;
- instr_data += sizeof(lx);
- len -= sizeof(lx);
- if (lx.stype != IWFFFF_STRU_LAYER) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return -EINVAL;
- }
- lp = kzalloc(sizeof(*lp), gfp_mask);
- if (lp == NULL) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return -ENOMEM;
- }
- if (prev_lp) {
- prev_lp->next = lp;
- } else {
- ip->layer = lp;
- }
- prev_lp = lp;
- lp->flags = lx.flags;
- lp->velocity_mode = lx.velocity_mode;
- lp->layer_event = lx.layer_event;
- lp->low_range = lx.low_range;
- lp->high_range = lx.high_range;
- lp->pan = lx.pan;
- lp->pan_freq_scale = lx.pan_freq_scale;
- lp->attenuation = lx.attenuation;
- snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
- snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
- lp->freq_scale = le16_to_cpu(lx.freq_scale);
- lp->freq_center = lx.freq_center;
- err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
- lp,
- &lp->penv, &lx.penv,
- &instr_data, &len,
- gfp_mask);
- if (err < 0) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return err;
- }
- err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
- lp,
- &lp->venv, &lx.venv,
- &instr_data, &len,
- gfp_mask);
- if (err < 0) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return err;
- }
- while (len > (long)sizeof(__u32)) {
- __u32 stype;
-
- if (copy_from_user(&stype, instr_data, sizeof(stype)))
- return -EFAULT;
- if (stype != IWFFFF_STRU_WAVE)
- break;
- err = snd_seq_iwffff_copy_wave_from_stream(ops,
- lp,
- &instr_data,
- &len,
- atomic);
- if (err < 0) {
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return err;
- }
- }
- }
- return 0;
-}
-
-static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx,
- struct iwffff_lfo *fp)
-{
- fx->freq = cpu_to_le16(fp->freq);
- fx->depth = cpu_to_le16(fp->depth);
- fx->sweep = cpu_to_le16(fp->sweep);
- fp->shape = fx->shape;
- fp->delay = fx->delay;
-}
-
-static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
- struct iwffff_layer *lp,
- struct iwffff_xenv *ex,
- struct iwffff_env *ep,
- char __user **data,
- long *len)
-{
- struct iwffff_env_record *rp;
- struct iwffff_xenv_record rx;
- struct iwffff_env_point *pp;
- struct iwffff_xenv_point px;
- int points_size, idx;
-
- ex->flags = ep->flags;
- ex->mode = ep->mode;
- ex->index = ep->index;
- for (rp = ep->record; rp; rp = rp->next) {
- if (*len < (long)sizeof(rx))
- return -ENOMEM;
- memset(&rx, 0, sizeof(rx));
- rx.stype = req_stype;
- rx.nattack = cpu_to_le16(rp->nattack);
- rx.nrelease = cpu_to_le16(rp->nrelease);
- rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
- rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
- rx.release_rate = cpu_to_le16(rp->release_rate);
- rx.hirange = cpu_to_le16(rp->hirange);
- if (copy_to_user(*data, &rx, sizeof(rx)))
- return -EFAULT;
- *data += sizeof(rx);
- *len -= sizeof(rx);
- points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
- if (*len < points_size)
- return -ENOMEM;
- pp = (struct iwffff_env_point *)(rp + 1);
- for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
- px.offset = cpu_to_le16(pp->offset);
- px.rate = cpu_to_le16(pp->rate);
- if (copy_to_user(*data, &px, sizeof(px)))
- return -EFAULT;
- *data += sizeof(px);
- *len -= sizeof(px);
- }
- }
- return 0;
-}
-
-static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops,
- struct iwffff_layer *lp,
- char __user **data,
- long *len,
- int atomic)
-{
- struct iwffff_wave *wp;
- struct iwffff_xwave xp;
- int err;
- unsigned int real_size;
-
- for (wp = lp->wave; wp; wp = wp->next) {
- if (*len < (long)sizeof(xp))
- return -ENOMEM;
- memset(&xp, 0, sizeof(xp));
- xp.stype = IWFFFF_STRU_WAVE;
- xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
- xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
- xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
- xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
- xp.format = cpu_to_le32(wp->format);
- if (wp->format & IWFFFF_WAVE_ROM)
- xp.offset = cpu_to_le32(wp->address.memory);
- xp.size = cpu_to_le32(wp->size);
- xp.start = cpu_to_le32(wp->start);
- xp.loop_start = cpu_to_le32(wp->loop_start);
- xp.loop_end = cpu_to_le32(wp->loop_end);
- xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
- xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
- xp.attenuation = wp->attenuation;
- xp.low_note = wp->low_note;
- xp.high_note = wp->high_note;
- if (copy_to_user(*data, &xp, sizeof(xp)))
- return -EFAULT;
- *data += sizeof(xp);
- *len -= sizeof(xp);
- real_size = snd_seq_iwffff_size(wp->size, wp->format);
- if (!(wp->format & IWFFFF_WAVE_ROM)) {
- if (*len < (long)real_size)
- return -ENOMEM;
- }
- if (ops->get_sample) {
- err = ops->get_sample(ops->private_data, wp,
- *data, real_size, atomic);
- if (err < 0)
- return err;
- }
- if (!(wp->format & IWFFFF_WAVE_ROM)) {
- *data += real_size;
- *len -= real_size;
- }
- }
- return 0;
-}
-
-static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len, int atomic, int cmd)
-{
- struct snd_iwffff_ops *ops = private_data;
- struct iwffff_instrument *ip;
- struct iwffff_xinstrument ix;
- struct iwffff_layer *lp;
- struct iwffff_xlayer lx;
- char __user *layer_instr_data;
- int err;
-
- if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
- return -EINVAL;
- if (len < (long)sizeof(ix))
- return -ENOMEM;
- memset(&ix, 0, sizeof(ix));
- ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
- ix.stype = IWFFFF_STRU_INSTR;
- ix.exclusion = cpu_to_le16(ip->exclusion);
- ix.layer_type = cpu_to_le16(ip->layer_type);
- ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
- ix.effect1 = cpu_to_le16(ip->effect1);
- ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
- ix.effect2 = ip->effect2;
- ix.effect2_depth = ip->effect2_depth;
- if (copy_to_user(instr_data, &ix, sizeof(ix)))
- return -EFAULT;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- for (lp = ip->layer; lp; lp = lp->next) {
- if (len < (long)sizeof(lx))
- return -ENOMEM;
- memset(&lx, 0, sizeof(lx));
- lx.stype = IWFFFF_STRU_LAYER;
- lx.flags = lp->flags;
- lx.velocity_mode = lp->velocity_mode;
- lx.layer_event = lp->layer_event;
- lx.low_range = lp->low_range;
- lx.high_range = lp->high_range;
- lx.pan = lp->pan;
- lx.pan_freq_scale = lp->pan_freq_scale;
- lx.attenuation = lp->attenuation;
- snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
- snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
- layer_instr_data = instr_data;
- instr_data += sizeof(lx);
- len -= sizeof(lx);
- err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
- lp,
- &lx.penv, &lp->penv,
- &instr_data, &len);
- if (err < 0)
- return err;
- err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
- lp,
- &lx.venv, &lp->venv,
- &instr_data, &len);
- if (err < 0)
- return err;
- /* layer structure updating is now finished */
- if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
- return -EFAULT;
- err = snd_seq_iwffff_copy_wave_to_stream(ops,
- lp,
- &instr_data,
- &len,
- atomic);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep)
-{
- long result = 0;
- struct iwffff_env_record *rp;
-
- for (rp = ep->record; rp; rp = rp->next) {
- result += sizeof(struct iwffff_xenv_record);
- result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
- }
- return 0;
-}
-
-static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp)
-{
- long result = 0;
- struct iwffff_wave *wp;
-
- for (wp = lp->wave; wp; wp = wp->next) {
- result += sizeof(struct iwffff_xwave);
- if (!(wp->format & IWFFFF_WAVE_ROM))
- result += wp->size;
- }
- return result;
-}
-
-static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr,
- long *size)
-{
- long result;
- struct iwffff_instrument *ip;
- struct iwffff_layer *lp;
-
- *size = 0;
- ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
- result = sizeof(struct iwffff_xinstrument);
- for (lp = ip->layer; lp; lp = lp->next) {
- result += sizeof(struct iwffff_xlayer);
- result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
- result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
- result += snd_seq_iwffff_wave_size_in_stream(lp);
- }
- *size = result;
- return 0;
-}
-
-static int snd_seq_iwffff_remove(void *private_data,
- struct snd_seq_kinstr *instr,
- int atomic)
-{
- struct snd_iwffff_ops *ops = private_data;
- struct iwffff_instrument *ip;
-
- ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
- snd_seq_iwffff_instr_free(ops, ip, atomic);
- return 0;
-}
-
-static void snd_seq_iwffff_notify(void *private_data,
- struct snd_seq_kinstr *instr,
- int what)
-{
- struct snd_iwffff_ops *ops = private_data;
-
- if (ops->notify)
- ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
- void *private_data,
- struct snd_seq_kinstr_ops *next)
-{
- memset(ops, 0, sizeof(*ops));
- ops->private_data = private_data;
- ops->kops.private_data = ops;
- ops->kops.add_len = sizeof(struct iwffff_instrument);
- ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
- ops->kops.put = snd_seq_iwffff_put;
- ops->kops.get = snd_seq_iwffff_get;
- ops->kops.get_size = snd_seq_iwffff_get_size;
- ops->kops.remove = snd_seq_iwffff_remove;
- ops->kops.notify = snd_seq_iwffff_notify;
- ops->kops.next = next;
- return 0;
-}
-
-/*
- * Init part
- */
-
-static int __init alsa_ainstr_iw_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ainstr_iw_exit(void)
-{
-}
-
-module_init(alsa_ainstr_iw_init)
-module_exit(alsa_ainstr_iw_exit)
-
-EXPORT_SYMBOL(snd_seq_iwffff_init);
diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c
deleted file mode 100644
index 6d6ffece7cb..00000000000
--- a/sound/core/seq/instr/ainstr_simple.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Simple (MOD player) - Instrument routines
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/ainstr_simple.h>
-#include <sound/initval.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
-MODULE_LICENSE("GPL");
-
-static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
-{
- unsigned int result = size;
-
- if (format & SIMPLE_WAVE_16BIT)
- result <<= 1;
- if (format & SIMPLE_WAVE_STEREO)
- result <<= 1;
- return result;
-}
-
-static void snd_seq_simple_instr_free(struct snd_simple_ops *ops,
- struct simple_instrument *ip,
- int atomic)
-{
- if (ops->remove_sample)
- ops->remove_sample(ops->private_data, ip, atomic);
-}
-
-static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len,
- int atomic, int cmd)
-{
- struct snd_simple_ops *ops = private_data;
- struct simple_instrument *ip;
- struct simple_xinstrument ix;
- int err;
- gfp_t gfp_mask;
- unsigned int real_size;
-
- if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
- return -EINVAL;
- gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
- /* copy instrument data */
- if (len < (long)sizeof(ix))
- return -EINVAL;
- if (copy_from_user(&ix, instr_data, sizeof(ix)))
- return -EFAULT;
- if (ix.stype != SIMPLE_STRU_INSTR)
- return -EINVAL;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- ip = (struct simple_instrument *)KINSTR_DATA(instr);
- ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
- ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
- ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
- ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
- ip->format = le32_to_cpu(ix.format);
- ip->size = le32_to_cpu(ix.size);
- ip->start = le32_to_cpu(ix.start);
- ip->loop_start = le32_to_cpu(ix.loop_start);
- ip->loop_end = le32_to_cpu(ix.loop_end);
- ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
- ip->effect1 = ix.effect1;
- ip->effect1_depth = ix.effect1_depth;
- ip->effect2 = ix.effect2;
- ip->effect2_depth = ix.effect2_depth;
- real_size = snd_seq_simple_size(ip->size, ip->format);
- if (len < (long)real_size)
- return -EINVAL;
- if (ops->put_sample) {
- err = ops->put_sample(ops->private_data, ip,
- instr_data, real_size, atomic);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr,
- char __user *instr_data, long len,
- int atomic, int cmd)
-{
- struct snd_simple_ops *ops = private_data;
- struct simple_instrument *ip;
- struct simple_xinstrument ix;
- int err;
- unsigned int real_size;
-
- if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
- return -EINVAL;
- if (len < (long)sizeof(ix))
- return -ENOMEM;
- memset(&ix, 0, sizeof(ix));
- ip = (struct simple_instrument *)KINSTR_DATA(instr);
- ix.stype = SIMPLE_STRU_INSTR;
- ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
- ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
- ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
- ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
- ix.format = cpu_to_le32(ip->format);
- ix.size = cpu_to_le32(ip->size);
- ix.start = cpu_to_le32(ip->start);
- ix.loop_start = cpu_to_le32(ip->loop_start);
- ix.loop_end = cpu_to_le32(ip->loop_end);
- ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
- ix.effect1 = cpu_to_le16(ip->effect1);
- ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
- ix.effect2 = ip->effect2;
- ix.effect2_depth = ip->effect2_depth;
- if (copy_to_user(instr_data, &ix, sizeof(ix)))
- return -EFAULT;
- instr_data += sizeof(ix);
- len -= sizeof(ix);
- real_size = snd_seq_simple_size(ip->size, ip->format);
- if (len < (long)real_size)
- return -ENOMEM;
- if (ops->get_sample) {
- err = ops->get_sample(ops->private_data, ip,
- instr_data, real_size, atomic);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr,
- long *size)
-{
- struct simple_instrument *ip;
-
- ip = (struct simple_instrument *)KINSTR_DATA(instr);
- *size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format);
- return 0;
-}
-
-static int snd_seq_simple_remove(void *private_data,
- struct snd_seq_kinstr *instr,
- int atomic)
-{
- struct snd_simple_ops *ops = private_data;
- struct simple_instrument *ip;
-
- ip = (struct simple_instrument *)KINSTR_DATA(instr);
- snd_seq_simple_instr_free(ops, ip, atomic);
- return 0;
-}
-
-static void snd_seq_simple_notify(void *private_data,
- struct snd_seq_kinstr *instr,
- int what)
-{
- struct snd_simple_ops *ops = private_data;
-
- if (ops->notify)
- ops->notify(ops->private_data, instr, what);
-}
-
-int snd_seq_simple_init(struct snd_simple_ops *ops,
- void *private_data,
- struct snd_seq_kinstr_ops *next)
-{
- memset(ops, 0, sizeof(*ops));
- ops->private_data = private_data;
- ops->kops.private_data = ops;
- ops->kops.add_len = sizeof(struct simple_instrument);
- ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
- ops->kops.put = snd_seq_simple_put;
- ops->kops.get = snd_seq_simple_get;
- ops->kops.get_size = snd_seq_simple_get_size;
- ops->kops.remove = snd_seq_simple_remove;
- ops->kops.notify = snd_seq_simple_notify;
- ops->kops.next = next;
- return 0;
-}
-
-/*
- * Init part
- */
-
-static int __init alsa_ainstr_simple_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ainstr_simple_exit(void)
-{
-}
-
-module_init(alsa_ainstr_simple_init)
-module_exit(alsa_ainstr_simple_exit)
-
-EXPORT_SYMBOL(snd_seq_simple_init);
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
index a37ddedf710..b38406b8463 100644
--- a/sound/core/seq/oss/Makefile
+++ b/sound/core/seq/oss/Makefile
@@ -1,6 +1,6 @@
#
# Makefile for ALSA
-# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 92858cf8b6e..16d42679e43 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -20,10 +20,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/minors.h>
@@ -41,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
@@ -166,7 +158,8 @@ odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_read(dp, buf, count);
}
@@ -176,7 +169,8 @@ odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offs
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_write(dp, buf, count, file);
}
@@ -185,7 +179,8 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return -EIO);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_ioctl(dp, cmd, arg);
}
@@ -200,7 +195,8 @@ odev_poll(struct file *file, poll_table * wait)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
- snd_assert(dp != NULL, return 0);
+ if (snd_BUG_ON(!dp))
+ return -ENXIO;
return snd_seq_oss_poll(dp, file, wait);
}
@@ -208,7 +204,7 @@ odev_poll(struct file *file, poll_table * wait)
* registration of sequencer minor device
*/
-static struct file_operations seq_oss_f_ops =
+static const struct file_operations seq_oss_f_ops =
{
.owner = THIS_MODULE,
.read = odev_read,
@@ -218,6 +214,7 @@ static struct file_operations seq_oss_f_ops =
.poll = odev_poll,
.unlocked_ioctl = odev_ioctl,
.compat_ioctl = odev_ioctl_compat,
+ .llseek = noop_llseek,
};
static int __init
@@ -228,22 +225,19 @@ register_device(void)
mutex_lock(&register_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(&register_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(&register_mutex);
return rc;
}
- debug_printk(("device registered\n"));
mutex_unlock(&register_mutex);
return 0;
}
@@ -252,11 +246,10 @@ static void
unregister_device(void)
{
mutex_lock(&register_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(&register_mutex);
}
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index 9a8567c928e..b4392432524 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -21,7 +21,6 @@
#ifndef __SEQ_OSS_DEVICE_H
#define __SEQ_OSS_DEVICE_H
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
@@ -32,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
@@ -47,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"
@@ -178,13 +173,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
/* misc. functions for proc interface */
char *enabled_str(int bool);
-
-/* for debug */
-#ifdef SNDRV_SEQ_OSS_DEBUG
-extern int seq_oss_debug;
-#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0)
-#else
-#define debug_printk(x) /**/
-#endif
-
#endif /* __SEQ_OSS_DEVICE_H */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
index 066f5f3e3f4..c3908862bc8 100644
--- a/sound/core/seq/oss/seq_oss_event.c
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -285,7 +285,12 @@ local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev
static int
note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ if (!snd_seq_oss_synth_is_valid(dp, dev))
+ return -ENXIO;
+
+ info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -340,7 +345,12 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
static int
note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ if (!snd_seq_oss_synth_is_valid(dp, dev))
+ return -ENXIO;
+
+ info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index ca5a2ed4d7c..b9184d20c39 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -28,7 +28,10 @@
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
/*
* common variables
@@ -58,6 +61,14 @@ static void free_devinfo(void *private);
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
+/* call snd_seq_oss_midi_lookup_ports() asynchronously */
+static void async_call_lookup_ports(struct work_struct *work)
+{
+ snd_seq_oss_midi_lookup_ports(system_client);
+}
+
+static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
+
/*
* create sequencer client for OSS sequencer
*/
@@ -81,10 +92,6 @@ snd_seq_oss_create_client(void)
goto __error;
system_client = rc;
- debug_printk(("new client = %d\n", rc));
-
- /* look up midi devices */
- snd_seq_oss_midi_lookup_ports(system_client);
/* create annoucement receiver port */
memset(port, 0, sizeof(*port));
@@ -113,6 +120,9 @@ snd_seq_oss_create_client(void)
}
rc = 0;
+ /* look up midi devices */
+ schedule_work(&async_lookup_work);
+
__error:
kfree(port);
return rc;
@@ -158,6 +168,7 @@ receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic
int
snd_seq_oss_delete_client(void)
{
+ cancel_work_sync(&async_lookup_work);
if (system_client >= 0)
snd_seq_delete_kernel_client(system_client);
@@ -176,49 +187,48 @@ snd_seq_oss_open(struct file *file, int level)
int i, rc;
struct seq_oss_devinfo *dp;
- if ((dp = kzalloc(sizeof(*dp), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc device info\n");
+ dp = kzalloc(sizeof(*dp), GFP_KERNEL);
+ if (!dp) {
+ 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;
+ dp->queue = -1;
for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
if (client_table[i] == NULL)
break;
}
- if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- snd_printk(KERN_ERR "too many applications\n");
- kfree(dp);
- return -ENOMEM;
- }
dp->index = i;
- dp->cseq = system_client;
- dp->port = -1;
- dp->queue = -1;
- dp->readq = NULL;
- dp->writeq = NULL;
+ if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
+ pr_err("ALSA: seq_oss: too many applications\n");
+ rc = -ENOMEM;
+ goto _error;
+ }
/* look up synth and midi devices */
snd_seq_oss_synth_setup(dp);
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"));
- if ((rc = create_port(dp)) < 0) {
- snd_printk(KERN_ERR "can't create port\n");
+ rc = create_port(dp);
+ if (rc < 0) {
+ pr_err("ALSA: seq_oss: can't create port\n");
goto _error;
}
/* allocate queue */
- debug_printk(("allocate queue\n"));
- if ((rc = alloc_seq_queue(dp)) < 0)
+ rc = alloc_seq_queue(dp);
+ if (rc < 0)
goto _error;
/* set address */
@@ -233,32 +243,30 @@ 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)) {
- if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
+ dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
+ if (!dp->readq) {
rc = -ENOMEM;
goto _error;
}
}
/* 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 == NULL) {
+ if (!dp->writeq) {
rc = -ENOMEM;
goto _error;
}
}
/* initialize timer */
- debug_printk(("initialize timer\n"));
- if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
- snd_printk(KERN_ERR "can't alloc timer\n");
+ dp->timer = snd_seq_oss_timer_new(dp);
+ if (!dp->timer) {
+ 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;
@@ -272,15 +280,13 @@ snd_seq_oss_open(struct file *file, int level)
client_table[dp->index] = dp;
num_clients++;
- debug_printk(("open done\n"));
return 0;
_error:
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
- i = dp->queue;
+ delete_seq_queue(dp->queue);
delete_port(dp);
- delete_seq_queue(i);
return rc;
}
@@ -332,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;
}
@@ -343,10 +348,11 @@ create_port(struct seq_oss_devinfo *dp)
static int
delete_port(struct seq_oss_devinfo *dp)
{
- if (dp->port < 0)
+ if (dp->port < 0) {
+ kfree(dp);
return 0;
+ }
- debug_printk(("delete_port %i\n", dp->port));
return snd_seq_event_port_detach(dp->cseq, dp->port);
}
@@ -384,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;
}
@@ -421,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"));
}
@@ -449,7 +450,6 @@ snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
return;
if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
dp->writeq) {
- debug_printk(("syncing..\n"));
while (snd_seq_oss_writeq_sync(dp->writeq))
;
}
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
index 5ac701c903c..5b8520177b0 100644
--- a/sound/core/seq/oss/seq_oss_ioctl.c
+++ b/sound/core/seq/oss/seq_oss_ioctl.c
@@ -90,12 +90,10 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
case SNDCTL_SEQ_PANIC:
- debug_printk(("panic\n"));
snd_seq_oss_reset(dp);
return -EINVAL;
case SNDCTL_SEQ_SYNC:
- debug_printk(("sync\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
while (snd_seq_oss_writeq_sync(dp->writeq))
@@ -105,55 +103,45 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_SEQ_RESET:
- debug_printk(("reset\n"));
snd_seq_oss_reset(dp);
return 0;
case SNDCTL_SEQ_TESTMIDI:
- debug_printk(("test midi\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
case SNDCTL_SEQ_GETINCOUNT:
- debug_printk(("get in count\n"));
if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
return 0;
return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETOUTCOUNT:
- debug_printk(("get out count\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETTIME:
- debug_printk(("get time\n"));
return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
case SNDCTL_SEQ_RESETSAMPLES:
- debug_printk(("reset samples\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
case SNDCTL_SEQ_NRSYNTHS:
- debug_printk(("nr synths\n"));
return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
case SNDCTL_SEQ_NRMIDIS:
- debug_printk(("nr midis\n"));
return put_user(dp->max_mididev, p) ? -EFAULT : 0;
case SNDCTL_SYNTH_MEMAVL:
- debug_printk(("mem avail\n"));
if (get_user(dev, p))
return -EFAULT;
val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
return put_user(val, p) ? -EFAULT : 0;
case SNDCTL_FM_4OP_ENABLE:
- debug_printk(("4op\n"));
if (get_user(dev, p))
return -EFAULT;
snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
@@ -161,19 +149,15 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
case SNDCTL_SYNTH_INFO:
case SNDCTL_SYNTH_ID:
- debug_printk(("synth info\n"));
return snd_seq_oss_synth_info_user(dp, arg);
case SNDCTL_SEQ_OUTOFBAND:
- debug_printk(("out of band\n"));
return snd_seq_oss_oob_user(dp, arg);
case SNDCTL_MIDI_INFO:
- debug_printk(("midi info\n"));
return snd_seq_oss_midi_info_user(dp, arg);
case SNDCTL_SEQ_THRESHOLD:
- debug_printk(("threshold\n"));
if (! is_write_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -186,7 +170,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_MIDI_PRETIME:
- debug_printk(("pretime\n"));
if (dp->readq == NULL || !is_read_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -199,7 +182,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return put_user(val, p) ? -EFAULT : 0;
default:
- debug_printk(("others\n"));
if (! is_write_mode(dp->file_mode))
return -EIO;
return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 0a711d2d04f..3a4569669ef 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <sound/asoundef.h>
#include "seq_oss_midi.h"
#include "seq_oss_readq.h"
#include "seq_oss_timer.h"
@@ -27,6 +28,7 @@
#include <sound/seq_midi_event.h>
#include "../seq_lock.h"
#include <linux/init.h>
+#include <linux/slab.h>
/*
@@ -70,7 +72,7 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
* look up the existing ports
* this looks a very exhausting job.
*/
-int __init
+int
snd_seq_oss_midi_lookup_ports(int client)
{
struct snd_seq_client_info *clinfo;
@@ -151,7 +153,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
struct seq_oss_midi *mdev;
unsigned long flags;
- debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
/* the port must include generic midi */
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
return 0;
@@ -173,7 +174,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
* allocate midi info record
*/
if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc midi info\n");
+ pr_err("ALSA: seq_oss: can't malloc midi info\n");
return -ENOMEM;
}
@@ -189,7 +190,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/* create MIDI coder */
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
- snd_printk(KERN_ERR "can't malloc midi coder\n");
+ pr_err("ALSA: seq_oss: can't malloc midi coder\n");
kfree(mdev);
return -ENOMEM;
}
@@ -404,7 +405,6 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
return 0;
}
- debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
memset(&subs, 0, sizeof(subs));
if (mdev->opened & PERM_WRITE) {
subs.sender = dp->addr;
@@ -468,7 +468,6 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
struct snd_seq_event ev;
int c;
- debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
memset(&ev, 0, sizeof(ev));
ev.dest.client = mdev->client;
ev.dest.port = mdev->port;
@@ -476,19 +475,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
ev.source.port = dp->port;
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
ev.type = SNDRV_SEQ_EVENT_SENSING;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
for (c = 0; c < 16; c++) {
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
ev.data.control.channel = c;
- ev.data.control.param = 123;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+ ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
- ev.data.control.param = 121;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+ ev.data.control.param =
+ MIDI_CTL_RESET_CONTROLLERS;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
ev.data.control.value = 0;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
}
}
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index f5de79f29f1..654d17a5023 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -25,6 +25,7 @@
#include <sound/seq_oss_legacy.h>
#include "../seq_lock.h"
#include <linux/wait.h>
+#include <linux/slab.h>
/*
* constants
@@ -47,12 +48,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
struct seq_oss_readq *q;
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue\n");
+ pr_err("ALSA: seq_oss: can't malloc read queue\n");
return NULL;
}
if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue buffer\n");
+ pr_err("ALSA: seq_oss: can't malloc read queue buffer\n");
kfree(q);
return NULL;
}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index ab570a0a618..701feb71b70 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -24,6 +24,8 @@
#include "seq_oss_midi.h"
#include "../seq_lock.h"
#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
/*
* constants
@@ -104,7 +106,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
unsigned long flags;
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc synth info\n");
+ pr_err("ALSA: seq_oss: can't malloc synth info\n");
return -ENOMEM;
}
rec->seq_device = -1;
@@ -128,7 +130,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
if (i >= max_synth_devs) {
if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "no more synth slot\n");
+ pr_err("ALSA: seq_oss: no more synth slot\n");
kfree(rec);
return -ENOMEM;
}
@@ -136,7 +138,6 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
}
rec->seq_device = i;
synth_devs[i] = rec;
- debug_printk(("synth %s registered %d\n", rec->name, i));
spin_unlock_irqrestore(&register_lock, flags);
dev->driver_data = rec;
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
@@ -161,7 +162,7 @@ snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
}
if (index >= max_synth_devs) {
spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "can't unregister synth\n");
+ pr_err("ALSA: seq_oss: can't unregister synth\n");
return -EINVAL;
}
synth_devs[index] = NULL;
@@ -245,11 +246,15 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
info->nr_voices = rec->nr_voices;
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
- if (!info->ch)
- BUG();
+ if (!info->ch) {
+ pr_err("ALSA: seq_oss: Cannot malloc voices\n");
+ rec->oper.close(&info->arg);
+ module_put(rec->oper.owner);
+ snd_use_lock_free(&rec->use_lock);
+ continue;
+ }
reset_channels(info);
}
- debug_printk(("synth %d assigned\n", i));
info->opened++;
rec->opened++;
dp->synth_opened++;
@@ -303,7 +308,8 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
+ if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
if (! info->opened)
@@ -318,7 +324,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
if (rec == NULL)
continue;
if (rec->opened > 0) {
- debug_printk(("synth %d closed\n", i));
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
rec->opened = 0;
@@ -397,7 +402,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
+ if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
+ return;
info = &dp->synths[dev];
if (! info->opened)
return;
@@ -599,6 +605,9 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
{
struct seq_oss_synth *rec;
+ if (dev < 0 || dev >= dp->max_synthdev)
+ return -ENXIO;
+
if (dp->synths[dev].is_midi) {
struct midi_info minf;
snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
index c440fdacec9..4f24ea9fad9 100644
--- a/sound/core/seq/oss/seq_oss_timer.c
+++ b/sound/core/seq/oss/seq_oss_timer.c
@@ -23,6 +23,7 @@
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <sound/seq_oss_legacy.h>
+#include <linux/slab.h>
/*
*/
@@ -232,7 +233,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
int value;
if (cmd == SNDCTL_SEQ_CTRLRATE) {
- debug_printk(("ctrl rate\n"));
/* if *arg == 0, just return the current rate */
if (get_user(value, arg))
return -EFAULT;
@@ -247,21 +247,16 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
switch (cmd) {
case SNDCTL_TMR_START:
- debug_printk(("timer start\n"));
return snd_seq_oss_timer_start(timer);
case SNDCTL_TMR_STOP:
- debug_printk(("timer stop\n"));
return snd_seq_oss_timer_stop(timer);
case SNDCTL_TMR_CONTINUE:
- debug_printk(("timer continue\n"));
return snd_seq_oss_timer_continue(timer);
case SNDCTL_TMR_TEMPO:
- debug_printk(("timer tempo\n"));
if (get_user(value, arg))
return -EFAULT;
return snd_seq_oss_timer_tempo(timer, value);
case SNDCTL_TMR_TIMEBASE:
- debug_printk(("timer timebase\n"));
if (get_user(value, arg))
return -EFAULT;
if (value < MIN_OSS_TIMEBASE)
@@ -275,7 +270,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SELECT:
case SNDCTL_TMR_SOURCE:
- debug_printk(("timer XXX\n"));
/* not supported */
return 0;
}
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
index 5c8495601a3..d50338bbc21 100644
--- a/sound/core/seq/oss/seq_oss_writeq.c
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -27,6 +27,7 @@
#include "../seq_lock.h"
#include "../seq_clientmgr.h"
#include <linux/wait.h>
+#include <linux/slab.h>
/*
@@ -63,8 +64,10 @@ snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
void
snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
{
- snd_seq_oss_writeq_clear(q); /* to be sure */
- kfree(q);
+ if (q) {
+ snd_seq_oss_writeq_clear(q); /* to be sure */
+ kfree(q);
+ }
}
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 2f0d8773ac6..71211056108 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -19,9 +19,9 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -33,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)
@@ -44,7 +45,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
int seq_default_timer_card = -1;
int seq_default_timer_device =
-#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
+#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
+ SNDRV_TIMER_GLOBAL_HRTIMER
+#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM
@@ -53,7 +56,7 @@ int seq_default_timer_device =
int seq_default_timer_subdevice = 0;
int seq_default_timer_resolution = 0; /* Hz */
-MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
MODULE_LICENSE("GPL");
@@ -72,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 532a660df51..225c73152ee 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1,7 +1,7 @@
/*
* ALSA sequencer Client Manager
* Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
*
*
@@ -21,9 +21,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/minors.h>
@@ -124,22 +123,20 @@ 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;
}
return clienttab[clientid];
}
-extern int seq_client_load[];
-
struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
{
unsigned long flags;
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;
}
@@ -152,14 +149,14 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
return NULL;
}
spin_unlock_irqrestore(&clients_lock, flags);
-#ifdef CONFIG_KMOD
- if (!in_interrupt() && current->fs->root) {
+#ifdef CONFIG_MODULES
+ if (!in_interrupt()) {
static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
static char card_requested[SNDRV_CARDS];
if (clientid < SNDRV_SEQ_GLOBAL_CLIENTS) {
int idx;
- if (! client_requested[clientid] && current->fs->root) {
+ if (!client_requested[clientid]) {
client_requested[clientid] = 1;
for (idx = 0; idx < 15; idx++) {
if (seq_client_load[idx] < 0)
@@ -270,7 +267,8 @@ static int seq_free_client1(struct snd_seq_client *client)
{
unsigned long flags;
- snd_assert(client != NULL, return -EINVAL);
+ if (!client)
+ return 0;
snd_seq_delete_all_ports(client);
snd_seq_queue_client_leave(client->number);
spin_lock_irqsave(&clients_lock, flags);
@@ -293,8 +291,8 @@ static void seq_free_client(struct snd_seq_client * client)
mutex_lock(&register_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:
@@ -303,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(&register_mutex);
@@ -321,6 +319,11 @@ static int snd_seq_open(struct inode *inode, struct file *file)
int c, mode; /* client id */
struct snd_seq_client *client;
struct snd_seq_user_client *user;
+ int err;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
if (mutex_lock_interruptible(&register_mutex))
return -ERESTARTSYS;
@@ -407,7 +410,8 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
return -EFAULT;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
return -ENXIO;
@@ -656,10 +660,9 @@ 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 list_head *p;
struct snd_seq_port_subs_info *grp;
src_port = snd_seq_port_use_ptr(client, event->source.port);
@@ -674,8 +677,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
read_lock(&grp->list_lock);
else
down_read(&grp->list_mutex);
- list_for_each(p, &grp->list_head) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(subs, &grp->list_head, src_list) {
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
@@ -683,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;
@@ -695,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;
}
@@ -707,30 +713,33 @@ 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 list_head *p;
+ struct snd_seq_client_port *port;
dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
if (dest_client == NULL)
return 0; /* no matching destination */
read_lock(&dest_client->ports_lock);
- list_for_each(p, &dest_client->ports_list_head) {
- struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &dest_client->ports_list_head, list) {
event->dest.port = port->addr.port;
/* pass NULL as source client to avoid error bounce */
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;
@@ -832,7 +845,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
struct snd_seq_client *client;
int result;
- snd_assert(cell != NULL, return -EINVAL);
+ if (snd_BUG_ON(!cell))
+ return -EINVAL;
client = snd_seq_client_use_ptr(cell->event.source.client);
if (client == NULL) {
@@ -970,8 +984,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev)
return -EINVAL;
break;
case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
- if (! snd_seq_ev_is_instr_type(ev) ||
- ! snd_seq_ev_is_direct(ev))
+ if (! snd_seq_ev_is_direct(ev))
return -EINVAL;
break;
}
@@ -1002,7 +1015,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
return -ENXIO;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if (!client->accept_output || client->pool == NULL)
return -ENXIO;
@@ -1051,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
@@ -1084,7 +1098,8 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
unsigned int mask = 0;
/* check client structures are in place */
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
client->data.user.fifo) {
@@ -2193,7 +2208,7 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
if (p->cmd == cmd)
return p->func(client, arg);
}
- snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
+ pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
return -ENOTTY;
}
@@ -2203,7 +2218,8 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg
{
struct snd_seq_client *client = file->private_data;
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
}
@@ -2224,7 +2240,8 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
struct snd_seq_client *client;
va_list args;
- snd_assert(! in_interrupt(), return -EBUSY);
+ if (snd_BUG_ON(in_interrupt()))
+ return -EBUSY;
if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD)
return -EINVAL;
@@ -2273,7 +2290,8 @@ int snd_seq_delete_kernel_client(int client)
{
struct snd_seq_client *ptr;
- snd_assert(! in_interrupt(), return -EBUSY);
+ if (snd_BUG_ON(in_interrupt()))
+ return -EBUSY;
ptr = clientptr(client);
if (ptr == NULL)
@@ -2296,7 +2314,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
struct snd_seq_client *cptr;
int result;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
if (ev->type == SNDRV_SEQ_EVENT_NONE)
return 0; /* ignore this */
@@ -2362,7 +2381,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
struct snd_seq_client *cptr;
int result;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
/* fill in client number */
ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
@@ -2400,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;
}
@@ -2473,11 +2493,10 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
struct snd_seq_client *client)
{
- struct list_head *l;
+ struct snd_seq_client_port *p;
mutex_lock(&client->ports_mutex);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n",
p->addr.port, p->name,
FLAG_PERM_RD(p->capability),
@@ -2491,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)
@@ -2542,13 +2558,14 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
* REGISTRATION PART
*/
-static struct file_operations snd_seq_f_ops =
+static const struct file_operations snd_seq_f_ops =
{
.owner = THIS_MODULE,
.read = snd_seq_read,
.write = snd_seq_write,
.open = snd_seq_open,
.release = snd_seq_release,
+ .llseek = no_llseek,
.poll = snd_seq_poll,
.unlocked_ioctl = snd_seq_ioctl,
.compat_ioctl = snd_seq_ioctl_compat,
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 5e04e20e239..20f0a725ec7 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -98,4 +98,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
+extern int seq_client_load[15];
+
#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 9628c06e4ea..81f7c109dc4 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -21,6 +21,7 @@
/* This file included from seq.c */
#include <linux/compat.h>
+#include <linux/slab.h>
struct snd_seq_port_info32 {
struct snd_seq_addr addr; /* client/port numbers */
@@ -48,12 +49,11 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
struct snd_seq_port_info *data;
mm_segment_t fs;
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (! data)
- return -ENOMEM;
+ data = memdup_user(data32, sizeof(*data32));
+ if (IS_ERR(data))
+ return PTR_ERR(data);
- if (copy_from_user(data, data32, sizeof(*data32)) ||
- get_user(data->flags, &data32->flags) ||
+ if (get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue))
goto error;
data->kernel = NULL;
@@ -92,7 +92,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
struct snd_seq_client *client = file->private_data;
void __user *argp = compat_ptr(arg);
- snd_assert(client != NULL, return -ENXIO);
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
switch (cmd) {
case SNDRV_SEQ_IOCTL_PVERSION:
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index b79d011813c..91a786a783e 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -36,8 +36,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/seq_device.h>
@@ -66,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 */
@@ -106,11 +106,10 @@ static void remove_drivers(void);
static void snd_seq_device_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *head;
+ struct ops_list *ops;
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
ops->id,
ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
@@ -126,7 +125,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
* load all registered drivers (called from seq_clientmgr.c)
*/
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
static int snd_seq_in_init;
void snd_seq_autoload_lock(void)
@@ -142,8 +141,8 @@ void snd_seq_autoload_unlock(void)
void snd_seq_device_load_drivers(void)
{
-#ifdef CONFIG_KMOD
- struct list_head *head;
+#ifdef CONFIG_MODULES
+ struct ops_list *ops;
/* Calling request_module during module_init()
* may cause blocking.
@@ -151,12 +150,8 @@ void snd_seq_device_load_drivers(void)
if (snd_seq_in_init)
return;
- if (! current->fs->root)
- return;
-
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
if (! (ops->driver & DRIVER_LOADED) &&
! (ops->driver & DRIVER_REQUESTED)) {
ops->used++;
@@ -173,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)
@@ -193,7 +188,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
if (result)
*result = NULL;
- snd_assert(id != NULL, return -EINVAL);
+ if (snd_BUG_ON(!id))
+ return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
@@ -238,7 +234,8 @@ static int snd_seq_device_free(struct snd_seq_device *dev)
{
struct ops_list *ops;
- snd_assert(dev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!dev))
+ return -EINVAL;
ops = find_driver(dev->id, 0);
if (ops == NULL)
@@ -314,8 +311,8 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
int argsize)
{
- struct list_head *head;
struct ops_list *ops;
+ struct snd_seq_device *dev;
if (id == NULL || entry == NULL ||
entry->init_device == NULL || entry->free_device == NULL)
@@ -328,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;
@@ -341,8 +338,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
ops->argsize = argsize;
/* initialize existing devices if necessary */
- list_for_each(head, &ops->dev_list) {
- struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+ list_for_each_entry(dev, &ops->dev_list, list) {
init_device(dev, ops);
}
mutex_unlock(&ops->reg_mutex);
@@ -394,15 +390,15 @@ static struct ops_list * create_driver(char *id)
*/
int snd_seq_device_unregister_driver(char *id)
{
- struct list_head *head;
struct ops_list *ops;
+ struct snd_seq_device *dev;
ops = find_driver(id, 0);
if (ops == NULL)
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;
@@ -411,14 +407,13 @@ int snd_seq_device_unregister_driver(char *id)
/* close and release all devices associated with this driver */
mutex_lock(&ops->reg_mutex);
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
- list_for_each(head, &ops->dev_list) {
- struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+ list_for_each_entry(dev, &ops->dev_list, list) {
free_device(dev, ops);
}
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);
@@ -464,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;
}
@@ -472,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);
}
@@ -491,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;
}
@@ -500,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);
}
@@ -512,11 +507,10 @@ static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
*/
static struct ops_list * find_driver(char *id, int create_if_empty)
{
- struct list_head *head;
+ struct ops_list *ops;
mutex_lock(&ops_mutex);
- list_for_each(head, &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
+ list_for_each_entry(ops, &opslist, list) {
if (strcmp(ops->id, id) == 0) {
ops->used++;
mutex_unlock(&ops_mutex);
@@ -565,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)
@@ -575,7 +569,7 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers);
EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index e55488d1237..ec667f158f1 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -18,10 +18,9 @@
*
*/
-#include <sound/driver.h>
#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>
@@ -47,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.
@@ -66,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");
@@ -199,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 6b055aed7a4..53a403e17c5 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/slab.h>
#include "seq_fifo.h"
@@ -35,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;
}
@@ -66,9 +65,11 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
{
struct snd_seq_fifo *f;
- snd_assert(fifo != NULL, return);
+ if (snd_BUG_ON(!fifo))
+ return;
f = *fifo;
- snd_assert(f != NULL, return);
+ if (snd_BUG_ON(!f))
+ return;
*fifo = NULL;
snd_seq_fifo_clear(f);
@@ -117,12 +118,13 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
unsigned long flags;
int err;
- snd_assert(f != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f))
+ return -EINVAL;
snd_use_lock_use(&f->use_lock);
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
if (err < 0) {
- if (err == -ENOMEM)
+ if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
snd_use_lock_free(&f->use_lock);
return err;
@@ -175,7 +177,8 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
unsigned long flags;
wait_queue_t wait;
- snd_assert(f != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f))
+ return -EINVAL;
*cellp = NULL;
init_waitqueue_entry(&wait, current);
@@ -234,7 +237,8 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
struct snd_seq_pool *newpool, *oldpool;
struct snd_seq_event_cell *cell, *next, *oldhead;
- snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!f || !f->pool))
+ return -EINVAL;
/* allocate new pool */
newpool = snd_seq_pool_new(poolsize);
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index 8a7fe5cca1c..acf7769419f 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -19,8 +19,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
+#include <linux/export.h>
#include <sound/core.h>
#include "seq_info.h"
diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c
deleted file mode 100644
index f30d171b6d9..00000000000
--- a/sound/core/seq/seq_instr.c
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * Generic Instrument routines for ALSA sequencer
- * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "seq_clientmgr.h"
-#include <sound/seq_instr.h>
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
-MODULE_LICENSE("GPL");
-
-
-static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
-{
- if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
- spin_lock_irqsave(&list->ops_lock, list->ops_flags);
- } else {
- mutex_lock(&list->ops_mutex);
- }
-}
-
-static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
-{
- if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
- spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
- } else {
- mutex_unlock(&list->ops_mutex);
- }
-}
-
-static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
-{
- struct snd_seq_kinstr *instr;
-
- instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
- if (instr == NULL)
- return NULL;
- instr->add_len = add_len;
- return instr;
-}
-
-static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
-{
- int result = 0;
-
- if (instr == NULL)
- return -EINVAL;
- if (instr->ops && instr->ops->remove)
- result = instr->ops->remove(instr->ops->private_data, instr, 1);
- if (!result)
- kfree(instr);
- return result;
-}
-
-struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
-{
- struct snd_seq_kinstr_list *list;
-
- list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
- if (list == NULL)
- return NULL;
- spin_lock_init(&list->lock);
- spin_lock_init(&list->ops_lock);
- mutex_init(&list->ops_mutex);
- list->owner = -1;
- return list;
-}
-
-void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
-{
- struct snd_seq_kinstr_list *list;
- struct snd_seq_kinstr *instr;
- struct snd_seq_kcluster *cluster;
- int idx;
- unsigned long flags;
-
- if (list_ptr == NULL)
- return;
- list = *list_ptr;
- *list_ptr = NULL;
- if (list == NULL)
- return;
-
- for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
- while ((instr = list->hash[idx]) != NULL) {
- list->hash[idx] = instr->next;
- list->count--;
- spin_lock_irqsave(&list->lock, flags);
- while (instr->use) {
- spin_unlock_irqrestore(&list->lock, flags);
- schedule_timeout_interruptible(1);
- spin_lock_irqsave(&list->lock, flags);
- }
- spin_unlock_irqrestore(&list->lock, flags);
- if (snd_seq_instr_free(instr, 0)<0)
- snd_printk(KERN_WARNING "instrument free problem\n");
- }
- while ((cluster = list->chash[idx]) != NULL) {
- list->chash[idx] = cluster->next;
- list->ccount--;
- kfree(cluster);
- }
- }
- kfree(list);
-}
-
-static int instr_free_compare(struct snd_seq_kinstr *instr,
- struct snd_seq_instr_header *ifree,
- unsigned int client)
-{
- switch (ifree->cmd) {
- case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
- /* all, except private for other clients */
- if ((instr->instr.std & 0xff000000) == 0)
- return 0;
- if (((instr->instr.std >> 24) & 0xff) == client)
- return 0;
- return 1;
- case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
- /* all my private instruments */
- if ((instr->instr.std & 0xff000000) == 0)
- return 1;
- if (((instr->instr.std >> 24) & 0xff) == client)
- return 0;
- return 1;
- case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
- /* all my private instruments */
- if ((instr->instr.std & 0xff000000) == 0) {
- if (instr->instr.cluster == ifree->id.cluster)
- return 0;
- return 1;
- }
- if (((instr->instr.std >> 24) & 0xff) == client) {
- if (instr->instr.cluster == ifree->id.cluster)
- return 0;
- }
- return 1;
- }
- return 1;
-}
-
-int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
- struct snd_seq_instr_header *ifree,
- int client,
- int atomic)
-{
- struct snd_seq_kinstr *instr, *prev, *next, *flist;
- int idx;
- unsigned long flags;
-
- snd_instr_lock_ops(list);
- for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
- spin_lock_irqsave(&list->lock, flags);
- instr = list->hash[idx];
- prev = flist = NULL;
- while (instr) {
- while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
- prev = instr;
- instr = instr->next;
- }
- if (instr == NULL)
- continue;
- if (instr->ops && instr->ops->notify)
- instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
- next = instr->next;
- if (prev == NULL) {
- list->hash[idx] = next;
- } else {
- prev->next = next;
- }
- list->count--;
- instr->next = flist;
- flist = instr;
- instr = next;
- }
- spin_unlock_irqrestore(&list->lock, flags);
- while (flist) {
- instr = flist;
- flist = instr->next;
- while (instr->use)
- schedule_timeout_interruptible(1);
- if (snd_seq_instr_free(instr, atomic)<0)
- snd_printk(KERN_WARNING "instrument free problem\n");
- instr = next;
- }
- }
- snd_instr_unlock_ops(list);
- return 0;
-}
-
-static int compute_hash_instr_key(struct snd_seq_instr *instr)
-{
- int result;
-
- result = instr->bank | (instr->prg << 16);
- result += result >> 24;
- result += result >> 16;
- result += result >> 8;
- return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
-}
-
-#if 0
-static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
-{
- int result;
-
- result = cluster;
- result += result >> 24;
- result += result >> 16;
- result += result >> 8;
- return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
-}
-#endif
-
-static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
-{
- if (exact) {
- if (i1->cluster != i2->cluster ||
- i1->bank != i2->bank ||
- i1->prg != i2->prg)
- return 1;
- if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
- return 1;
- if (!(i1->std & i2->std))
- return 1;
- return 0;
- } else {
- unsigned int client_check;
-
- if (i2->cluster && i1->cluster != i2->cluster)
- return 1;
- client_check = i2->std & 0xff000000;
- if (client_check) {
- if ((i1->std & 0xff000000) != client_check)
- return 1;
- } else {
- if ((i1->std & i2->std) != i2->std)
- return 1;
- }
- return i1->bank != i2->bank || i1->prg != i2->prg;
- }
-}
-
-struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
- struct snd_seq_instr *instr,
- int exact,
- int follow_alias)
-{
- unsigned long flags;
- int depth = 0;
- struct snd_seq_kinstr *result;
-
- if (list == NULL || instr == NULL)
- return NULL;
- spin_lock_irqsave(&list->lock, flags);
- __again:
- result = list->hash[compute_hash_instr_key(instr)];
- while (result) {
- if (!compare_instr(&result->instr, instr, exact)) {
- if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
- instr = (struct snd_seq_instr *)KINSTR_DATA(result);
- if (++depth > 10)
- goto __not_found;
- goto __again;
- }
- result->use++;
- spin_unlock_irqrestore(&list->lock, flags);
- return result;
- }
- result = result->next;
- }
- __not_found:
- spin_unlock_irqrestore(&list->lock, flags);
- return NULL;
-}
-
-void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
- struct snd_seq_kinstr *instr)
-{
- unsigned long flags;
-
- if (list == NULL || instr == NULL)
- return;
- spin_lock_irqsave(&list->lock, flags);
- if (instr->use <= 0) {
- snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
- } else {
- instr->use--;
- }
- spin_unlock_irqrestore(&list->lock, flags);
-}
-
-static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
- char *instr_type)
-{
- while (ops) {
- if (!strcmp(ops->instr_type, instr_type))
- return ops;
- ops = ops->next;
- }
- return NULL;
-}
-
-static int instr_result(struct snd_seq_event *ev,
- int type, int result,
- int atomic)
-{
- struct snd_seq_event sev;
-
- memset(&sev, 0, sizeof(sev));
- sev.type = SNDRV_SEQ_EVENT_RESULT;
- sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
- SNDRV_SEQ_PRIORITY_NORMAL;
- sev.source = ev->dest;
- sev.dest = ev->source;
- sev.data.result.event = type;
- sev.data.result.result = result;
-#if 0
- printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
- type, result,
- sev.queue,
- sev.source.client, sev.source.port,
- sev.dest.client, sev.dest.port);
-#endif
- return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
-}
-
-static int instr_begin(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&list->lock, flags);
- if (list->owner >= 0 && list->owner != ev->source.client) {
- spin_unlock_irqrestore(&list->lock, flags);
- return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
- }
- list->owner = ev->source.client;
- spin_unlock_irqrestore(&list->lock, flags);
- return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
-}
-
-static int instr_end(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- unsigned long flags;
-
- /* TODO: timeout handling */
- spin_lock_irqsave(&list->lock, flags);
- if (list->owner == ev->source.client) {
- list->owner = -1;
- spin_unlock_irqrestore(&list->lock, flags);
- return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
- }
- spin_unlock_irqrestore(&list->lock, flags);
- return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
-}
-
-static int instr_info(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_format_info(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_reset(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_status(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_put(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- unsigned long flags;
- struct snd_seq_instr_header put;
- struct snd_seq_kinstr *instr;
- int result = -EINVAL, len, key;
-
- if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
- goto __return;
-
- if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
- goto __return;
- if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
- sizeof(struct snd_seq_instr_header))) {
- result = -EFAULT;
- goto __return;
- }
- snd_instr_lock_ops(list);
- if (put.id.instr.std & 0xff000000) { /* private instrument */
- put.id.instr.std &= 0x00ffffff;
- put.id.instr.std |= (unsigned int)ev->source.client << 24;
- }
- if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
- snd_seq_instr_free_use(list, instr);
- snd_instr_unlock_ops(list);
- result = -EBUSY;
- goto __return;
- }
- ops = instr_ops(ops, put.data.data.format);
- if (ops == NULL) {
- snd_instr_unlock_ops(list);
- goto __return;
- }
- len = ops->add_len;
- if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
- len = sizeof(struct snd_seq_instr);
- instr = snd_seq_instr_new(len, atomic);
- if (instr == NULL) {
- snd_instr_unlock_ops(list);
- result = -ENOMEM;
- goto __return;
- }
- instr->ops = ops;
- instr->instr = put.id.instr;
- strlcpy(instr->name, put.data.name, sizeof(instr->name));
- instr->type = put.data.type;
- if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
- result = ops->put(ops->private_data,
- instr,
- (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
- ev->data.ext.len - sizeof(struct snd_seq_instr_header),
- atomic,
- put.cmd);
- if (result < 0) {
- snd_seq_instr_free(instr, atomic);
- snd_instr_unlock_ops(list);
- goto __return;
- }
- }
- key = compute_hash_instr_key(&instr->instr);
- spin_lock_irqsave(&list->lock, flags);
- instr->next = list->hash[key];
- list->hash[key] = instr;
- list->count++;
- spin_unlock_irqrestore(&list->lock, flags);
- snd_instr_unlock_ops(list);
- result = 0;
- __return:
- instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
- return result;
-}
-
-static int instr_get(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_free(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- struct snd_seq_instr_header ifree;
- struct snd_seq_kinstr *instr, *prev;
- int result = -EINVAL;
- unsigned long flags;
- unsigned int hash;
-
- if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
- goto __return;
-
- if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
- goto __return;
- if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
- sizeof(struct snd_seq_instr_header))) {
- result = -EFAULT;
- goto __return;
- }
- if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
- ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
- ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
- result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
- goto __return;
- }
- if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
- if (ifree.id.instr.std & 0xff000000) {
- ifree.id.instr.std &= 0x00ffffff;
- ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
- }
- hash = compute_hash_instr_key(&ifree.id.instr);
- snd_instr_lock_ops(list);
- spin_lock_irqsave(&list->lock, flags);
- instr = list->hash[hash];
- prev = NULL;
- while (instr) {
- if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
- goto __free_single;
- prev = instr;
- instr = instr->next;
- }
- result = -ENOENT;
- spin_unlock_irqrestore(&list->lock, flags);
- snd_instr_unlock_ops(list);
- goto __return;
-
- __free_single:
- if (prev) {
- prev->next = instr->next;
- } else {
- list->hash[hash] = instr->next;
- }
- if (instr->ops && instr->ops->notify)
- instr->ops->notify(instr->ops->private_data, instr,
- SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
- while (instr->use) {
- spin_unlock_irqrestore(&list->lock, flags);
- schedule_timeout_interruptible(1);
- spin_lock_irqsave(&list->lock, flags);
- }
- spin_unlock_irqrestore(&list->lock, flags);
- result = snd_seq_instr_free(instr, atomic);
- snd_instr_unlock_ops(list);
- goto __return;
- }
-
- __return:
- instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
- return result;
-}
-
-static int instr_list(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-static int instr_cluster(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int atomic, int hop)
-{
- return -ENXIO;
-}
-
-int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
- struct snd_seq_kinstr_list *list,
- struct snd_seq_event *ev,
- int client,
- int atomic,
- int hop)
-{
- int direct = 0;
-
- snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
- if (snd_seq_ev_is_direct(ev)) {
- direct = 1;
- switch (ev->type) {
- case SNDRV_SEQ_EVENT_INSTR_BEGIN:
- return instr_begin(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_END:
- return instr_end(ops, list, ev, atomic, hop);
- }
- }
- if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
- return -EINVAL;
- switch (ev->type) {
- case SNDRV_SEQ_EVENT_INSTR_INFO:
- return instr_info(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_FINFO:
- return instr_format_info(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_RESET:
- return instr_reset(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_STATUS:
- return instr_status(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_PUT:
- return instr_put(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_GET:
- return instr_get(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_FREE:
- return instr_free(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_LIST:
- return instr_list(ops, list, ev, atomic, hop);
- case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
- return instr_cluster(ops, list, ev, atomic, hop);
- }
- return -EINVAL;
-}
-
-/*
- * Init part
- */
-
-static int __init alsa_seq_instr_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_seq_instr_exit(void)
-{
-}
-
-module_init(alsa_seq_instr_init)
-module_exit(alsa_seq_instr_exit)
-
-EXPORT_SYMBOL(snd_seq_instr_list_new);
-EXPORT_SYMBOL(snd_seq_instr_list_free);
-EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
-EXPORT_SYMBOL(snd_seq_instr_find);
-EXPORT_SYMBOL(snd_seq_instr_free_use);
-EXPORT_SYMBOL(snd_seq_instr_event);
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 1a34941d421..3b693e924db 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -19,7 +19,7 @@
*
*/
-#include <sound/driver.h>
+#include <linux/export.h>
#include <sound/core.h>
#include "seq_lock.h"
@@ -31,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 4bffe509f71..1e206de0c2d 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -1,7 +1,7 @@
/*
* ALSA sequencer Memory Manager
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
* 2000 by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
@@ -20,8 +20,8 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <sound/core.h>
@@ -87,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)
@@ -151,14 +151,14 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
return len;
newlen = len;
if (size_aligned > 0)
- newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
+ newlen = roundup(len, size_aligned);
if (count < newlen)
return -EAGAIN;
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;
}
@@ -188,9 +188,11 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
unsigned long flags;
struct snd_seq_pool *pool;
- snd_assert(cell != NULL, return);
+ if (snd_BUG_ON(!cell))
+ return;
pool = cell->pool;
- snd_assert(pool != NULL, return);
+ if (snd_BUG_ON(!pool))
+ return;
spin_lock_irqsave(&pool->lock, flags);
free_cell(pool, cell);
@@ -234,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;
}
@@ -342,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;
}
@@ -379,13 +381,14 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
struct snd_seq_event_cell *cellptr;
unsigned long flags;
- snd_assert(pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pool))
+ return -EINVAL;
if (pool->ptr) /* should be atomic? */
return 0;
pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
if (pool->ptr == NULL) {
- snd_printd("seq: malloc for sequencer events failed\n");
+ pr_debug("ALSA: seq: malloc for sequencer events failed\n");
return -ENOMEM;
}
@@ -415,7 +418,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
struct snd_seq_event_cell *ptr;
int max_count = 5 * HZ;
- snd_assert(pool != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pool))
+ return -EINVAL;
/* wait for closing all threads */
spin_lock_irqsave(&pool->lock, flags);
@@ -427,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);
@@ -460,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 1daa5b069c7..a1fd77af605 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -1,7 +1,7 @@
/*
* Generic MIDI synth driver for ALSA sequencer
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
*
* 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
@@ -26,12 +26,11 @@ Possible options for midisynth module:
*/
-#include <sound/driver.h>
#include <linux/init.h>
#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>
@@ -40,7 +39,7 @@ Possible options for midisynth module:
#include <sound/seq_midi_event.h>
#include <sound/initval.h>
-MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
MODULE_LICENSE("GPL");
static int output_buffer_size = PAGE_SIZE;
@@ -117,10 +116,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
struct snd_rawmidi_runtime *runtime;
int tmp;
- snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+ if (snd_BUG_ON(!substream || !buf))
+ return -EINVAL;
runtime = substream->runtime;
if ((tmp = runtime->avail) < count) {
- snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+ if (printk_ratelimit())
+ pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
return -ENOMEM;
}
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
@@ -136,14 +137,15 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
struct snd_rawmidi_substream *substream;
int len;
- snd_assert(msynth != NULL, return -EINVAL);
+ if (snd_BUG_ON(!msynth))
+ return -EINVAL;
substream = msynth->output_rfile.output;
if (substream == NULL)
return -ENODEV;
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
/* invalid event */
- snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+ pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
return 0;
}
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
@@ -187,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;
@@ -211,7 +213,8 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri
int err;
struct seq_midisynth *msynth = private_data;
- snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+ if (snd_BUG_ON(!msynth->input_rfile.input))
+ return -EINVAL;
err = snd_rawmidi_kernel_release(&msynth->input_rfile);
return err;
}
@@ -228,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
msynth->subdevice,
SNDRV_RAWMIDI_LFLG_OUTPUT,
&msynth->output_rfile)) < 0) {
- snd_printd("midi output open failed!!!\n");
+ pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
}
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = output_buffer_size;
+ params.no_active_sensing = 1;
if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
snd_rawmidi_kernel_release(&msynth->output_rfile);
return err;
@@ -246,11 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
- unsigned char buf = 0xff; /* MIDI reset */
- snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
- /* sending single MIDI reset message to shut the device up */
- snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
+ if (snd_BUG_ON(!msynth->output_rfile.output))
+ return -EINVAL;
snd_rawmidi_drain_output(msynth->output_rfile.output);
return snd_rawmidi_kernel_release(&msynth->output_rfile);
}
@@ -286,7 +288,8 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
int device = dev->device;
unsigned int input_count = 0, output_count = 0;
- snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+ if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
+ return -EINVAL;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
@@ -359,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 d7c4fb86b9e..9b6470cdcf2 100644
--- a/sound/core/seq/seq_midi_emul.c
+++ b/sound/core/seq/seq_midi_emul.c
@@ -29,10 +29,10 @@
* code in here. If there is it should be reported as a bug.
*/
-#include <sound/driver.h>
#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>
@@ -71,7 +71,7 @@ static void reset_all_channels(struct snd_midi_channel_set *chset);
* such as GM, GS and XG.
* There modes that this module will run in are:
* Generic MIDI - no interpretation at all, it will just save current values
- * of controlers etc.
+ * of controllers etc.
* GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN,
* SysEx will be interpreded as defined in General Midi.
* GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
@@ -89,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)
@@ -98,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;
}
@@ -176,7 +176,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
ev->data.control.value);
break;
case SNDRV_SEQ_EVENT_NONREGPARAM:
- /* Break it back into its controler values */
+ /* Break it back into its controller values */
chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
= (ev->data.control.value >> 7) & 0x7f;
@@ -189,7 +189,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
nrpn(ops, drv, chan, chanset);
break;
case SNDRV_SEQ_EVENT_REGPARAM:
- /* Break it back into its controler values */
+ /* Break it back into its controller values */
chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
= (ev->data.control.value >> 7) & 0x7f;
@@ -229,17 +229,10 @@ snd_midi_process_event(struct snd_midi_op *ops,
case SNDRV_SEQ_EVENT_PORT_START:
case SNDRV_SEQ_EVENT_PORT_EXIT:
case SNDRV_SEQ_EVENT_PORT_CHANGE:
- case SNDRV_SEQ_EVENT_SAMPLE:
- case SNDRV_SEQ_EVENT_SAMPLE_START:
- case SNDRV_SEQ_EVENT_SAMPLE_STOP:
- case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
- case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
- case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
- case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
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;
}
}
@@ -267,7 +260,7 @@ note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
}
/*
- * Do all driver independent operations for this controler and pass
+ * Do all driver independent operations for this controller and pass
* events that need to take place immediately to the driver.
*/
static void
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index 5ff80b77690..37db7ba492a 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -2,7 +2,7 @@
* MIDI byte <-> sequencer event coder
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
*
* 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
@@ -19,23 +19,22 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#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>
#include <sound/asoundef.h>
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
MODULE_LICENSE("GPL");
-/* queue type */
-/* from 0 to 7 are normal commands (note off, on, etc.) */
-#define ST_NOTEOFF 0
-#define ST_NOTEON 1
+/* event type, index into status_event[] */
+/* from 0 to 6 are normal commands (note off, on, etc.) for 0x9?-0xe? */
+#define ST_INVALID 7
#define ST_SPECIAL 8
#define ST_SYSEX ST_SPECIAL
/* from 8 to 15 are events for 0xf0-0xf7 */
@@ -65,32 +64,33 @@ static struct status_event_list {
void (*encode)(struct snd_midi_event *dev, struct snd_seq_event *ev);
void (*decode)(struct snd_seq_event *ev, unsigned char *buf);
} status_event[] = {
- /* 0x80 - 0xf0 */
- {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode},
- {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode},
- {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode},
- {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode},
- {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode},
- {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode},
- {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode},
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */
+ /* 0x80 - 0xef */
+ {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode},
+ {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode},
+ {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode},
+ {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode},
+ {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode},
+ {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode},
+ {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode},
+ /* invalid */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL},
/* 0xf0 - 0xff */
- {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */
- {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */
- {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */
- {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */
- {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */
- {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */
- {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */
- {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */
- {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */
- {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */
- {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */
- {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */
+ {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */
+ {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */
+ {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */
+ {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf4 */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf5 */
+ {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf7 */
+ {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf9 */
+ {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */
+ {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */
+ {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */
+ {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xfd */
+ {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */
+ {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */
};
static int extra_decode_ctrl14(struct snd_midi_event *dev, unsigned char *buf, int len,
@@ -129,6 +129,7 @@ int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev)
}
dev->bufsize = bufsize;
dev->lastcmd = 0xff;
+ dev->type = ST_INVALID;
spin_lock_init(&dev->lock);
*rdev = dev;
return 0;
@@ -149,7 +150,7 @@ static inline void reset_encode(struct snd_midi_event *dev)
{
dev->read = 0;
dev->qlen = 0;
- dev->type = 0;
+ dev->type = ST_INVALID;
}
void snd_midi_event_reset_encode(struct snd_midi_event *dev)
@@ -251,29 +252,31 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
- return 1;
+ return ev->type != SNDRV_SEQ_EVENT_NONE;
}
spin_lock_irqsave(&dev->lock, flags);
- if (dev->qlen > 0) {
- /* rest of command */
- dev->buf[dev->read++] = c;
- if (dev->type != ST_SYSEX)
- dev->qlen--;
- } else {
+ if ((c & 0x80) &&
+ (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
/* new command */
+ dev->buf[0] = c;
+ if ((c & 0xf0) == 0xf0) /* system messages */
+ dev->type = (c & 0x0f) + ST_SPECIAL;
+ else
+ dev->type = (c >> 4) & 0x07;
dev->read = 1;
- if (c & 0x80) {
- dev->buf[0] = c;
- if ((c & 0xf0) == 0xf0) /* special events */
- dev->type = (c & 0x0f) + ST_SPECIAL;
- else
- dev->type = (c >> 4) & 0x07;
- dev->qlen = status_event[dev->type].qlen;
- } else {
- /* process this byte as argument */
+ dev->qlen = status_event[dev->type].qlen;
+ } else {
+ if (dev->qlen > 0) {
+ /* rest of command */
dev->buf[dev->read++] = c;
+ if (dev->type != ST_SYSEX)
+ dev->qlen--;
+ } else {
+ /* running status */
+ dev->buf[1] = c;
dev->qlen = status_event[dev->type].qlen - 1;
+ dev->read = 2;
}
}
if (dev->qlen == 0) {
@@ -282,6 +285,8 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
if (status_event[dev->type].encode) /* set data values */
status_event[dev->type].encode(dev, ev);
+ if (dev->type >= ST_SPECIAL)
+ dev->type = ST_INVALID;
rc = 1;
} else if (dev->type == ST_SYSEX) {
if (c == MIDI_CMD_COMMON_SYSEX_END ||
@@ -500,10 +505,10 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
if (dev->nostat && count < 12)
return -ENOMEM;
cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
- bytes[0] = ev->data.control.param & 0x007f;
- bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
- bytes[2] = ev->data.control.value & 0x007f;
- bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+ bytes[0] = (ev->data.control.param & 0x3f80) >> 7;
+ bytes[1] = ev->data.control.param & 0x007f;
+ bytes[2] = (ev->data.control.value & 0x3f80) >> 7;
+ bytes[3] = ev->data.control.value & 0x007f;
if (cmd != dev->lastcmd && !dev->nostat) {
if (count < 9)
return -ENOMEM;
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 8c64b58ff77..794a341bf0e 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -1,7 +1,7 @@
/*
* ALSA sequencer Ports
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -20,9 +20,9 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "seq_system.h"
#include "seq_ports.h"
#include "seq_clientmgr.h"
@@ -59,14 +59,12 @@ much elements are in array.
struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
int num)
{
- struct list_head *p;
struct snd_seq_client_port *port;
if (client == NULL)
return NULL;
read_lock(&client->ports_lock);
- list_for_each(p, &client->ports_list_head) {
- port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port == num) {
if (port->closing)
break; /* deleting now */
@@ -85,14 +83,12 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
struct snd_seq_port_info *pinfo)
{
int num;
- struct list_head *p;
struct snd_seq_client_port *port, *found;
num = pinfo->addr.port;
found = NULL;
read_lock(&client->ports_lock);
- list_for_each(p, &client->ports_list_head) {
- port = list_entry(p, struct snd_seq_client_port, list);
+ list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port < num)
continue;
if (port->addr.port == num) {
@@ -131,22 +127,22 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port)
{
unsigned long flags;
- struct snd_seq_client_port *new_port;
- struct list_head *l;
+ struct snd_seq_client_port *new_port, *p;
int num = -1;
/* sanity check */
- snd_assert(client, return NULL);
+ if (snd_BUG_ON(!client))
+ return NULL;
if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
- snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
+ pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
return NULL;
}
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
if (! new_port) {
- snd_printd("malloc failed for registering client port\n");
+ pr_debug("ALSA: seq: malloc failed for registering client port\n");
return NULL; /* failure, out of memory */
}
/* init port data */
@@ -161,15 +157,14 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
num = port >= 0 ? port : 0;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port > num)
break;
if (port < 0) /* auto-probe mode */
num = p->addr.port + 1;
}
/* insert the new port */
- list_add_tail(&new_port->list, l);
+ list_add_tail(&new_port->list, &p->list);
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
write_unlock_irqrestore(&client->ports_lock, flags);
@@ -251,9 +246,9 @@ static void clear_subscriber_list(struct snd_seq_client *client,
list_del(&subs->dest_list);
else
list_del(&subs->src_list);
+ up_write(&agrp->list_mutex);
unsubscribe_port(c, aport, agrp, &subs->info, 1);
kfree(subs);
- up_write(&agrp->list_mutex);
snd_seq_port_unlock(aport);
snd_seq_client_unlock(c);
}
@@ -275,8 +270,8 @@ static int port_delete(struct snd_seq_client *client,
if (port->private_free)
port->private_free(port->private_data);
- snd_assert(port->c_src.count == 0,);
- snd_assert(port->c_dest.count == 0,);
+ snd_BUG_ON(port->c_src.count != 0);
+ snd_BUG_ON(port->c_dest.count != 0);
kfree(port);
return 0;
@@ -287,16 +282,14 @@ static int port_delete(struct snd_seq_client *client,
int snd_seq_delete_port(struct snd_seq_client *client, int port)
{
unsigned long flags;
- struct list_head *l;
- struct snd_seq_client_port *found = NULL;
+ struct snd_seq_client_port *found = NULL, *p;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
- list_for_each(l, &client->ports_list_head) {
- struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+ list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
/* ok found. delete from the list at first */
- list_del(l);
+ list_del(&p->list);
client->num_ports--;
found = p;
break;
@@ -314,7 +307,8 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
int snd_seq_delete_all_ports(struct snd_seq_client *client)
{
unsigned long flags;
- struct list_head deleted_list, *p, *n;
+ struct list_head deleted_list;
+ struct snd_seq_client_port *port, *tmp;
/* move the port list to deleted_list, and
* clear the port list in the client data.
@@ -331,9 +325,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
write_unlock_irqrestore(&client->ports_lock, flags);
/* remove each port in deleted_list */
- list_for_each_safe(p, n, &deleted_list) {
- struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
- list_del(p);
+ list_for_each_entry_safe(port, tmp, &deleted_list, list) {
+ list_del(&port->list);
snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
port_delete(client, port);
}
@@ -345,7 +338,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
int snd_seq_set_port_info(struct snd_seq_client_port * port,
struct snd_seq_port_info * info)
{
- snd_assert(port && info, return -EINVAL);
+ if (snd_BUG_ON(!port || !info))
+ return -EINVAL;
/* set port name */
if (info->name[0])
@@ -374,7 +368,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
int snd_seq_get_port_info(struct snd_seq_client_port * port,
struct snd_seq_port_info * info)
{
- snd_assert(port && info, return -EINVAL);
+ if (snd_BUG_ON(!port || !info))
+ return -EINVAL;
/* get port name */
strlcpy(info->name, port->name, sizeof(info->name));
@@ -418,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,
@@ -500,8 +495,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
{
struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs;
- struct list_head *p;
+ struct snd_seq_subscribers *subs, *s;
int err, src_called = 0;
unsigned long flags;
int exclusive;
@@ -525,13 +519,11 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
if (src->exclusive || dest->exclusive)
goto __error;
/* check whether already exists */
- list_for_each(p, &src->list_head) {
- struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(s, &src->list_head, src_list) {
if (match_subs_info(info, &s->info))
goto __error;
}
- list_for_each(p, &dest->list_head) {
- struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, dest_list);
+ list_for_each_entry(s, &dest->list_head, dest_list) {
if (match_subs_info(info, &s->info))
goto __error;
}
@@ -582,7 +574,6 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
- struct list_head *p;
int err = -ENOENT;
unsigned long flags;
@@ -590,8 +581,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
/* look for the connection */
- list_for_each(p, &src->list_head) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(subs, &src->list_head, src_list) {
if (match_subs_info(info, &subs->info)) {
write_lock_irqsave(&src->list_lock, flags);
// write_lock(&dest->list_lock); // no lock yet
@@ -620,12 +610,10 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr)
{
- struct list_head *p;
struct snd_seq_subscribers *s, *found = NULL;
down_read(&src_grp->list_mutex);
- list_for_each(p, &src_grp->list_head) {
- s = list_entry(p, struct snd_seq_subscribers, src_list);
+ list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
found = s;
break;
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 074418617ee..021b02bc933 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -61,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;
}
@@ -80,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;
}
@@ -154,8 +153,8 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
int count;
int prior;
- snd_assert(f, return -EINVAL);
- snd_assert(cell, return -EINVAL);
+ if (snd_BUG_ON(!f || !cell))
+ return -EINVAL;
/* check flags */
prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
@@ -198,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;
}
}
@@ -224,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);
@@ -249,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;
@@ -260,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;
@@ -322,7 +321,8 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
freeprev = cell;
} else {
#if 0
- printk("type = %i, source = %i, dest = %i, client = %i\n",
+ pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
+ "client = %i\n",
cell->event.type,
cell->event.source.client,
cell->event.dest.client,
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 9b87bb0c7f3..aad4878cee5 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -35,7 +35,6 @@
* - Addition of experimental sync support.
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -113,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;
}
@@ -316,7 +315,8 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
int dest, err;
struct snd_seq_queue *q;
- snd_assert(cell != NULL, return -EINVAL);
+ if (snd_BUG_ON(!cell))
+ return -EINVAL;
dest = cell->event.queue; /* destination queue */
q = queueptr(dest);
if (q == NULL)
@@ -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;
@@ -735,7 +733,8 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
{
struct snd_seq_queue *q;
- snd_assert(ev != NULL, return -EINVAL);
+ if (snd_BUG_ON(!ev))
+ return -EINVAL;
q = queueptr(ev->data.queue.queue);
if (q == NULL)
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index b201b76e941..8ce1d0b40dc 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -19,8 +19,9 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include "seq_system.h"
#include "seq_timer.h"
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index b4b9a132cb1..e73605393ee 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -1,7 +1,7 @@
/*
* ALSA sequencer Timer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- * Jaroslav Kysela <perex@suse.cz>
+ * Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -20,20 +20,12 @@
*
*/
-#include <sound/driver.h>
#include <sound/core.h>
#include <linux/slab.h>
#include "seq_timer.h"
#include "seq_queue.h"
#include "seq_info.h"
-extern int seq_default_timer_class;
-extern int seq_default_timer_sclass;
-extern int seq_default_timer_card;
-extern int seq_default_timer_device;
-extern int seq_default_timer_subdevice;
-extern int seq_default_timer_resolution;
-
/* allowed sequencer timer frequencies, in Hz */
#define MIN_FREQUENCY 10
#define MAX_FREQUENCY 6250
@@ -41,22 +33,21 @@ extern int seq_default_timer_resolution;
#define SKEW_BASE 0x10000 /* 16bit shift */
-static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick,
- int tempo, int ppq)
+static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
{
- if (tempo < 1000000)
- tick->resolution = (tempo * 1000) / ppq;
+ if (tmr->tempo < 1000000)
+ tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
else {
/* might overflow.. */
unsigned int s;
- s = tempo % ppq;
- s = (s * 1000) / ppq;
- tick->resolution = (tempo / ppq) * 1000;
- tick->resolution += s;
+ s = tmr->tempo % tmr->ppq;
+ s = (s * 1000) / tmr->ppq;
+ tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+ tmr->tick.resolution += s;
}
- if (tick->resolution <= 0)
- tick->resolution = 1;
- snd_seq_timer_update_tick(tick, 0);
+ if (tmr->tick.resolution <= 0)
+ tmr->tick.resolution = 1;
+ snd_seq_timer_update_tick(&tmr->tick, 0);
}
/* create new timer (constructor) */
@@ -66,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);
@@ -87,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;
@@ -104,7 +95,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
- snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+ snd_seq_timer_set_tick_resolution(tmr);
tmr->running = 0;
tmr->type = SNDRV_SEQ_TIMER_ALSA;
@@ -181,13 +172,14 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tempo <= 0)
return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
if ((unsigned int)tempo != tmr->tempo) {
tmr->tempo = tempo;
- snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+ snd_seq_timer_set_tick_resolution(tmr);
}
spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
@@ -198,7 +190,8 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (ppq <= 0)
return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
@@ -206,12 +199,12 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
/* refuse to change ppq on running timers */
/* because it will upset the song position (ticks) */
spin_unlock_irqrestore(&tmr->lock, flags);
- snd_printd("seq: cannot change ppq of a running timer\n");
+ pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
return -EBUSY;
}
tmr->ppq = ppq;
- snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+ snd_seq_timer_set_tick_resolution(tmr);
spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -222,7 +215,8 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
tmr->tick.cur_tick = position;
@@ -237,7 +231,8 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
snd_seq_sanity_real_time(&position);
spin_lock_irqsave(&tmr->lock, flags);
@@ -252,11 +247,12 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
{
unsigned long flags;
- snd_assert(tmr, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
/* FIXME */
if (base != SKEW_BASE) {
- snd_printd("invalid skew base 0x%x\n", base);
+ pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
return -EINVAL;
}
spin_lock_irqsave(&tmr->lock, flags);
@@ -273,7 +269,8 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
int err;
tmr = q->timer;
- snd_assert(tmr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tmr->timeri)
return -EBUSY;
sprintf(str, "sequencer queue %i", q->queue);
@@ -293,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;
@@ -310,7 +307,8 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
struct snd_seq_timer *tmr;
tmr = q->timer;
- snd_assert(tmr != NULL, return -EINVAL);
+ if (snd_BUG_ON(!tmr))
+ return -EINVAL;
if (tmr->timeri) {
snd_timer_stop(tmr->timeri);
snd_timer_close(tmr->timeri);
@@ -336,7 +334,8 @@ static int initialize_timer(struct snd_seq_timer *tmr)
unsigned long freq;
t = tmr->timeri->timer;
- snd_assert(t, return -EINVAL);
+ if (snd_BUG_ON(!t))
+ return -EINVAL;
freq = tmr->preferred_resolution;
if (!freq)
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index e9ee1543c92..88dfb71805a 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -138,4 +138,11 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigne
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
+extern int seq_default_timer_class;
+extern int seq_default_timer_sclass;
+extern int seq_default_timer_card;
+extern int seq_default_timer_device;
+extern int seq_default_timer_subdevice;
+extern int seq_default_timer_resolution;
+
#endif
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 0cfa06c6b81..56e0f4cd3f8 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -35,10 +35,9 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/wait.h>
-#include <linux/sched.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -81,13 +80,11 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
struct snd_seq_event *ev)
{
struct snd_virmidi *vmidi;
- struct list_head *list;
unsigned char msg[4];
int len;
read_lock(&rdev->filelist_lock);
- list_for_each(list, &rdev->filelist) {
- vmidi = list_entry(list, struct snd_virmidi, list);
+ list_for_each_entry(vmidi, &rdev->filelist, list) {
if (!vmidi->trigger)
continue;
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
@@ -366,7 +363,7 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
if (rdev->client >= 0)
return 0;
- pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
+ pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
if (!pinfo) {
err = -ENOMEM;
goto __error;
@@ -383,7 +380,6 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
rdev->client = client;
/* create a port */
- memset(pinfo, 0, sizeof(*pinfo));
pinfo->addr.client = client;
sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
/* set all capabilities */
@@ -450,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 c30669f14ac..0a418503ec4 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -22,12 +22,13 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/memalloc.h>
/* table entries are align to 32 */
#define SGBUF_TBL_ALIGN 32
-#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
+#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
{
@@ -38,17 +39,20 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
if (! sgbuf)
return -EINVAL;
+ if (dmab->area)
+ vunmap(dmab->area);
+ dmab->area = NULL;
+
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
tmpb.dev.dev = sgbuf->dev;
for (i = 0; i < sgbuf->pages; i++) {
+ if (!(sgbuf->table[i].addr & ~PAGE_MASK))
+ continue; /* continuous pages */
tmpb.area = sgbuf->table[i].buf;
- tmpb.addr = sgbuf->table[i].addr;
- tmpb.bytes = PAGE_SIZE;
+ tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
+ tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
snd_dma_free_pages(&tmpb);
}
- if (dmab->area)
- vunmap(dmab->area);
- dmab->area = NULL;
kfree(sgbuf->table);
kfree(sgbuf->page_table);
@@ -58,13 +62,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
return 0;
}
+#define MAX_ALLOC_PAGES 32
+
void *snd_malloc_sgbuf_pages(struct device *device,
size_t size, struct snd_dma_buffer *dmab,
size_t *res_size)
{
struct snd_sg_buf *sgbuf;
- unsigned int i, pages;
+ unsigned int i, pages, chunk, maxpages;
struct snd_dma_buffer tmpb;
+ struct snd_sg_page *table;
+ struct page **pgtable;
dmab->area = NULL;
dmab->addr = 0;
@@ -74,34 +82,84 @@ void *snd_malloc_sgbuf_pages(struct device *device,
sgbuf->dev = device;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
- sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL);
- if (! sgbuf->table)
+ table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
+ if (!table)
goto _failed;
- sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL);
- if (! sgbuf->page_table)
+ sgbuf->table = table;
+ pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
+ if (!pgtable)
goto _failed;
+ sgbuf->page_table = pgtable;
- /* allocate each page */
- for (i = 0; i < pages; i++) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) {
- if (res_size == NULL)
+ /* allocate pages */
+ maxpages = MAX_ALLOC_PAGES;
+ while (pages > 0) {
+ chunk = pages;
+ /* don't be too eager to take a huge chunk */
+ if (chunk > maxpages)
+ chunk = maxpages;
+ chunk <<= PAGE_SHIFT;
+ if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
+ chunk, &tmpb) < 0) {
+ if (!sgbuf->pages)
+ goto _failed;
+ if (!res_size)
goto _failed;
- *res_size = size = sgbuf->pages * PAGE_SIZE;
+ size = sgbuf->pages * PAGE_SIZE;
break;
}
- sgbuf->table[i].buf = tmpb.area;
- sgbuf->table[i].addr = tmpb.addr;
- sgbuf->page_table[i] = virt_to_page(tmpb.area);
- sgbuf->pages++;
+ chunk = tmpb.bytes >> PAGE_SHIFT;
+ for (i = 0; i < chunk; i++) {
+ table->buf = tmpb.area;
+ table->addr = tmpb.addr;
+ if (!i)
+ table->addr |= chunk; /* mark head */
+ table++;
+ *pgtable++ = virt_to_page(tmpb.area);
+ tmpb.area += PAGE_SIZE;
+ tmpb.addr += PAGE_SIZE;
+ }
+ sgbuf->pages += chunk;
+ pages -= chunk;
+ if (chunk < maxpages)
+ maxpages = chunk;
}
sgbuf->size = size;
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
if (! dmab->area)
goto _failed;
+ if (res_size)
+ *res_size = sgbuf->size;
return dmab->area;
_failed:
snd_free_sgbuf_pages(dmab); /* free the table */
return NULL;
}
+
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ struct snd_sg_buf *sg = dmab->private_data;
+ unsigned int start, end, pg;
+
+ start = ofs >> PAGE_SHIFT;
+ end = (ofs + size - 1) >> PAGE_SHIFT;
+ /* check page continuity */
+ pg = sg->table[start].addr >> PAGE_SHIFT;
+ for (;;) {
+ start++;
+ if (start > end)
+ break;
+ pg++;
+ if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+ return (start << PAGE_SHIFT) - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 282742022de..38ad1a0dd3f 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -1,6 +1,6 @@
/*
* Advanced Linux Sound Architecture
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,30 +19,26 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
#include <linux/mutex.h>
-#define SNDRV_OS_MINORS 256
-
static int major = CONFIG_SND_MAJOR;
int snd_major;
EXPORT_SYMBOL(snd_major);
static int cards_limit = 1;
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
MODULE_LICENSE("GPL");
module_param(major, int, 0444);
@@ -61,19 +57,17 @@ EXPORT_SYMBOL(snd_ecards_limit);
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
/**
* snd_request_card - try to load the card module
* @card: the card number
*
* Tries to load the module "snd-card-X" for the given card number
- * via KMOD. Returns immediately if already loaded.
+ * via request_module. Returns immediately if already loaded.
*/
void snd_request_card(int card)
{
- if (! current->fs->root)
- return;
if (snd_card_locked(card))
return;
if (card < 0 || card >= cards_limit)
@@ -87,8 +81,6 @@ static void snd_request_other(int minor)
{
char *str;
- if (! current->fs->root)
- return;
switch (minor) {
case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break;
case SNDRV_MINOR_TIMER: str = "snd-timer"; break;
@@ -97,7 +89,7 @@ static void snd_request_other(int minor)
request_module(str);
}
-#endif /* request_module support */
+#endif /* modular kernel */
/**
* snd_lookup_minor_data - get user data of a registered device
@@ -106,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)
{
@@ -116,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;
@@ -126,63 +127,81 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
EXPORT_SYMBOL(snd_lookup_minor_data);
+#ifdef CONFIG_MODULES
+static struct snd_minor *autoload_device(unsigned int minor)
+{
+ int dev;
+ mutex_unlock(&sound_mutex); /* release lock temporarily */
+ dev = SNDRV_MINOR_DEVICE(minor);
+ if (dev == SNDRV_MINOR_CONTROL) {
+ /* /dev/aloadC? */
+ int card = SNDRV_MINOR_CARD(minor);
+ if (snd_cards[card] == NULL)
+ snd_request_card(card);
+ } else if (dev == SNDRV_MINOR_GLOBAL) {
+ /* /dev/aloadSEQ */
+ snd_request_other(minor);
+ }
+ mutex_lock(&sound_mutex); /* reacuire lock */
+ return snd_minors[minor];
+}
+#else /* !CONFIG_MODULES */
+#define autoload_device(minor) NULL
+#endif /* CONFIG_MODULES */
+
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
- const struct file_operations *old_fops;
+ const struct file_operations *new_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
+ mutex_lock(&sound_mutex);
mptr = snd_minors[minor];
if (mptr == NULL) {
-#ifdef CONFIG_KMOD
- int dev = SNDRV_MINOR_DEVICE(minor);
- if (dev == SNDRV_MINOR_CONTROL) {
- /* /dev/aloadC? */
- int card = SNDRV_MINOR_CARD(minor);
- if (snd_cards[card] == NULL)
- snd_request_card(card);
- } else if (dev == SNDRV_MINOR_GLOBAL) {
- /* /dev/aloadSEQ */
- snd_request_other(minor);
- }
-#ifndef CONFIG_SND_DYNAMIC_MINORS
- /* /dev/snd/{controlC?,seq} */
- mptr = snd_minors[minor];
- if (mptr == NULL)
-#endif
-#endif
+ mptr = autoload_device(minor);
+ if (!mptr) {
+ mutex_unlock(&sound_mutex);
return -ENODEV;
+ }
}
- old_fops = file->f_op;
- file->f_op = fops_get(mptr->f_ops);
+ new_fops = fops_get(mptr->f_ops);
+ mutex_unlock(&sound_mutex);
+ if (!new_fops)
+ return -ENODEV;
+ replace_fops(file, new_fops);
+
if (file->f_op->open)
err = file->f_op->open(inode, file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
return err;
}
-static struct file_operations snd_fops =
+static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
- .open = snd_open
+ .open = snd_open,
+ .llseek = noop_llseek,
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
-static int snd_find_free_minor(void)
+static int snd_find_free_minor(int type)
{
int minor;
+ /* static minors for module auto loading */
+ if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
+ return SNDRV_MINOR_SEQUENCER;
+ if (type == SNDRV_DEVICE_TYPE_TIMER)
+ return SNDRV_MINOR_TIMER;
+
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
- /* skip minors still used statically for autoloading devices */
- if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
- minor == SNDRV_MINOR_SEQUENCER)
+ /* skip static minors still used for module auto loading */
+ if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
+ continue;
+ if (minor == SNDRV_MINOR_SEQUENCER ||
+ minor == SNDRV_MINOR_TIMER)
continue;
if (!snd_minors[minor])
return minor;
@@ -200,47 +219,53 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
minor = type;
break;
case SNDRV_DEVICE_TYPE_CONTROL:
- snd_assert(card != NULL, return -EINVAL);
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
minor = SNDRV_MINOR(card->number, type);
break;
case SNDRV_DEVICE_TYPE_HWDEP:
case SNDRV_DEVICE_TYPE_RAWMIDI:
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
- snd_assert(card != NULL, return -EINVAL);
+ case SNDRV_DEVICE_TYPE_COMPRESS:
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
minor = SNDRV_MINOR(card->number, type + dev);
break;
default:
return -EINVAL;
}
- snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+ if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
+ return -EINVAL;
return minor;
}
#endif
/**
- * snd_register_device - Register the ALSA device file for the card
+ * snd_register_device_for_dev - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
* @name: the device file name
+ * @device: the &struct device to link this new device to
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
- * Retrurns 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(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name)
+int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
+ const struct file_operations *f_ops,
+ void *private_data,
+ const char *name, struct device *device)
{
int minor;
struct snd_minor *preg;
- struct device *device = NULL;
- snd_assert(name, return -EINVAL);
+ if (snd_BUG_ON(!name))
+ return -EINVAL;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
@@ -249,9 +274,10 @@ int snd_register_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_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])
@@ -263,18 +289,21 @@ int snd_register_device(int type, struct snd_card *card, int dev,
return minor;
}
snd_minors[minor] = preg;
- if (card)
- device = card->dev;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
- "%s", name);
- if (preg->dev)
- dev_set_drvdata(preg->dev, private_data);
+ private_data, "%s", name);
+ if (IS_ERR(preg->dev)) {
+ snd_minors[minor] = NULL;
+ mutex_unlock(&sound_mutex);
+ minor = PTR_ERR(preg->dev);
+ kfree(preg);
+ return minor;
+ }
mutex_unlock(&sound_mutex);
return 0;
}
-EXPORT_SYMBOL(snd_register_device);
+EXPORT_SYMBOL(snd_register_device_for_dev);
/* find the matching minor record
* return the index of snd_minor, or -1 if not found
@@ -303,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)
{
@@ -326,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
/*
@@ -429,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) {
@@ -438,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;
}
@@ -447,9 +479,8 @@ static void __exit alsa_sound_exit(void)
{
snd_info_minor_unregister();
snd_info_done();
- if (unregister_chrdev(major, "alsa") != 0)
- snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
+ unregister_chrdev(major, "alsa");
}
-module_init(alsa_sound_init)
-module_exit(alsa_sound_exit)
+subsys_initcall(alsa_sound_init);
+module_exit(alsa_sound_exit);
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index b2fc40aa520..573a65eb2b7 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -1,6 +1,6 @@
/*
* Advanced Linux Sound Architecture
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,15 +19,14 @@
*
*/
-#include <sound/driver.h>
-
#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>
@@ -36,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;
@@ -50,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;
@@ -66,7 +70,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
switch (type) {
case SNDRV_OSS_DEVICE_TYPE_MIXER:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
break;
case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
@@ -76,11 +81,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
minor = SNDRV_MINOR_OSS_MUSIC;
break;
case SNDRV_OSS_DEVICE_TYPE_PCM:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
break;
case SNDRV_OSS_DEVICE_TYPE_MIDI:
- snd_assert(card != NULL && dev <= 1, return -EINVAL);
+ if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+ return -EINVAL;
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
break;
case SNDRV_OSS_DEVICE_TYPE_DMFM:
@@ -92,13 +99,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
default:
return -EINVAL;
}
- snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL);
+ if (minor < 0 || minor >= SNDRV_OSS_MINORS)
+ return -EINVAL;
return minor;
}
int snd_register_oss_device(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name)
+ const struct file_operations *f_ops, void *private_data)
{
int minor = snd_oss_kernel_minor(type, card, dev);
int minor_unit;
@@ -106,9 +113,9 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
int cidx = SNDRV_MINOR_OSS_CARD(minor);
int track2 = -1;
int register1 = -1, register2 = -1;
- struct device *carddev = NULL;
+ 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;
@@ -120,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);
@@ -134,8 +142,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
break;
}
- if (card)
- carddev = card->dev;
register1 = register_sound_special_device(f_ops, minor, carddev);
if (register1 != minor)
goto __end;
@@ -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 10a79aed33f..777a45e08e5 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1,6 +1,6 @@
/*
* Timers abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -19,14 +19,13 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#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>
@@ -35,24 +34,27 @@
#include <sound/minors.h>
#include <sound/initval.h>
#include <linux/kmod.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
-#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
#endif
static int timer_limit = DEFAULT_TIMER_LIMIT;
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+static int timer_tstamp_monotonic = 1;
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ALSA timer interface");
MODULE_LICENSE("GPL");
module_param(timer_limit, int, 0444);
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;
@@ -130,11 +132,8 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
{
struct snd_timer *timer = NULL;
- struct list_head *p;
-
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->tmr_class != tid->dev_class)
continue;
if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
@@ -151,12 +150,10 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
return NULL;
}
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
static void snd_timer_request(struct snd_timer_id *tid)
{
- if (! current->fs->root)
- return;
switch (tid->dev_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
if (tid->device < timer_limit)
@@ -184,18 +181,14 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
{
struct snd_timer *timer;
struct snd_timer_instance *master;
- struct list_head *p, *q;
/* FIXME: it's really dumb to look up all entries.. */
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
- list_for_each(q, &timer->open_list_head) {
- master = list_entry(q, struct snd_timer_instance, open_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
+ 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;
@@ -214,16 +207,13 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
*/
static void snd_timer_check_master(struct snd_timer_instance *master)
{
- struct snd_timer_instance *slave;
- struct list_head *p, *n;
+ struct snd_timer_instance *slave, *tmp;
/* check all pending slaves */
- list_for_each_safe(p, n, &snd_timer_slave_list) {
- slave = list_entry(p, struct snd_timer_instance, open_list);
+ list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
if (slave->slave_class == master->slave_class &&
slave->slave_id == master->slave_id) {
- list_del(p);
- list_add_tail(p, &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;
@@ -250,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(&register_mutex);
@@ -272,8 +263,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
/* open a master instance */
mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
-#ifdef CONFIG_KMOD
- if (timer == NULL) {
+#ifdef CONFIG_MODULES
+ if (!timer) {
mutex_unlock(&register_mutex);
snd_timer_request(tid);
mutex_lock(&register_mutex);
@@ -317,10 +308,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri,
int snd_timer_close(struct snd_timer_instance *timeri)
{
struct snd_timer *timer = NULL;
- struct list_head *p, *n;
- struct snd_timer_instance *slave;
+ struct snd_timer_instance *slave, *tmp;
- snd_assert(timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timeri))
+ return -ENXIO;
/* force to stop the timer */
snd_timer_stop(timeri);
@@ -339,6 +330,8 @@ int snd_timer_close(struct snd_timer_instance *timeri)
mutex_unlock(&register_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) {
@@ -353,18 +346,18 @@ int snd_timer_close(struct snd_timer_instance *timeri)
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */
- list_for_each_safe(p, n, &timeri->slave_list_head) {
- slave = list_entry(p, struct snd_timer_instance, open_list);
+ list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
+ open_list) {
spin_lock_irq(&slave_active_lock);
_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
- list_del(p);
- list_add_tail(p, &snd_timer_slave_list);
+ list_move_tail(&slave->open_list, &snd_timer_slave_list);
slave->master = NULL;
slave->timer = NULL;
spin_unlock_irq(&slave_active_lock);
}
mutex_unlock(&register_mutex);
}
+ out:
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
@@ -394,17 +387,20 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
- struct list_head *n;
struct timespec tstamp;
- getnstimeofday(&tstamp);
- snd_assert(event >= SNDRV_TIMER_EVENT_START &&
- event <= SNDRV_TIMER_EVENT_PAUSE, return);
+ if (timer_tstamp_monotonic)
+ ktime_get_ts(&tstamp);
+ else
+ getnstimeofday(&tstamp);
+ if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
+ event > SNDRV_TIMER_EVENT_PAUSE))
+ return;
if (event == SNDRV_TIMER_EVENT_START ||
event == SNDRV_TIMER_EVENT_CONTINUE)
resolution = snd_timer_resolution(ti);
if (ti->ccallback)
- ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution);
+ ti->ccallback(ti, event, &tstamp, resolution);
if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
return;
timer = ti->timer;
@@ -413,19 +409,16 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
spin_lock_irqsave(&timer->lock, flags);
- list_for_each(n, &ti->slave_active_head) {
- ts = list_entry(n, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ti, event + 100, &tstamp, resolution);
- }
spin_unlock_irqrestore(&timer->lock, flags);
}
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;
@@ -489,7 +482,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
struct snd_timer *timer;
unsigned long flags;
- snd_assert(timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timeri))
+ return -ENXIO;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
if (!keep_flag) {
@@ -542,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;
@@ -593,10 +589,8 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
{
struct snd_timer_instance *ti;
unsigned long ticks = ~0UL;
- struct list_head *p;
- list_for_each(p, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->flags & SNDRV_TIMER_IFLG_START) {
ti->flags &= ~SNDRV_TIMER_IFLG_START;
ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
@@ -661,9 +655,9 @@ static void snd_timer_tasklet(unsigned long arg)
*/
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
- struct snd_timer_instance *ti, *ts;
+ struct snd_timer_instance *ti, *ts, *tmp;
unsigned long resolution, ticks;
- struct list_head *p, *q, *n, *ack_list_head;
+ struct list_head *p, *ack_list_head;
unsigned long flags;
int use_tasklet = 0;
@@ -679,12 +673,12 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
resolution = timer->hw.resolution;
/* loop for all active instances
- * Here we cannot use list_for_each because the active_list of a
+ * Here we cannot use list_for_each_entry because the active_list of a
* processed instance is relinked to done_list_head before the callback
* is called.
*/
- list_for_each_safe(p, n, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
+ active_list) {
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -700,7 +694,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
if (--timer->running)
- list_del(p);
+ list_del(&ti->active_list);
}
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -709,8 +703,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ack_list_head = &timer->sack_list_head;
if (list_empty(&ti->ack_list))
list_add_tail(&ti->ack_list, ack_list_head);
- list_for_each(q, &ti->slave_active_head) {
- ts = list_entry(q, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list) {
ts->pticks = ti->pticks;
ts->resolution = resolution;
if (list_empty(&ts->ack_list))
@@ -758,7 +751,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
spin_unlock_irqrestore(&timer->lock, flags);
if (use_tasklet)
- tasklet_hi_schedule(&timer->task_queue);
+ tasklet_schedule(&timer->task_queue);
}
/*
@@ -776,12 +769,13 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
.dev_disconnect = snd_timer_dev_disconnect,
};
- snd_assert(tid != NULL, return -EINVAL);
- snd_assert(rtimer != NULL, return -EINVAL);
- *rtimer = NULL;
+ if (snd_BUG_ON(!tid))
+ return -EINVAL;
+ if (rtimer)
+ *rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (timer == NULL) {
- snd_printk(KERN_ERR "timer: cannot allocate\n");
+ pr_err("ALSA: timer: cannot allocate\n");
return -ENOMEM;
}
timer->tmr_class = tid->dev_class;
@@ -806,19 +800,21 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
return err;
}
}
- *rtimer = timer;
+ if (rtimer)
+ *rtimer = timer;
return 0;
}
static int snd_timer_free(struct snd_timer *timer)
{
- snd_assert(timer != NULL, return -ENXIO);
+ if (!timer)
+ return 0;
mutex_lock(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
struct list_head *p, *n;
struct snd_timer_instance *ti;
- snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+ pr_warn("ALSA: timer %p is busy?\n", timer);
list_for_each_safe(p, n, &timer->open_list_head) {
list_del_init(p);
ti = list_entry(p, struct snd_timer_instance, open_list);
@@ -844,17 +840,15 @@ static int snd_timer_dev_register(struct snd_device *dev)
{
struct snd_timer *timer = dev->device_data;
struct snd_timer *timer1;
- struct list_head *p;
- snd_assert(timer != NULL && timer->hw.start != NULL &&
- timer->hw.stop != NULL, return -ENXIO);
+ if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
+ return -ENXIO;
if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
!timer->hw.resolution && timer->hw.c_resolution == NULL)
return -EINVAL;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_timer_list) {
- timer1 = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer1, &snd_timer_list, device_list) {
if (timer1->tmr_class > timer->tmr_class)
break;
if (timer1->tmr_class < timer->tmr_class)
@@ -877,7 +871,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
mutex_unlock(&register_mutex);
return -EBUSY;
}
- list_add_tail(&timer->device_list, p);
+ list_add_tail(&timer->device_list, &timer1->device_list);
mutex_unlock(&register_mutex);
return 0;
}
@@ -896,12 +890,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
- struct list_head *p, *n;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
- snd_assert(event >= SNDRV_TIMER_EVENT_MSTART &&
- event <= SNDRV_TIMER_EVENT_MRESUME, return);
+ if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
+ event > SNDRV_TIMER_EVENT_MRESUME))
+ return;
spin_lock_irqsave(&timer->lock, flags);
if (event == SNDRV_TIMER_EVENT_MSTART ||
event == SNDRV_TIMER_EVENT_MCONTINUE ||
@@ -911,15 +905,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
else
resolution = timer->hw.resolution;
}
- list_for_each(p, &timer->active_list_head) {
- ti = list_entry(p, struct snd_timer_instance, active_list);
+ list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->ccallback)
ti->ccallback(ti, event, tstamp, resolution);
- list_for_each(n, &ti->slave_active_head) {
- ts = list_entry(n, struct snd_timer_instance, active_list);
+ list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ts, event, tstamp, resolution);
- }
}
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -1057,11 +1048,9 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
{
struct snd_timer *timer;
struct snd_timer_instance *ti;
- struct list_head *p, *q;
mutex_lock(&register_mutex);
- list_for_each(p, &snd_timer_list) {
- timer = list_entry(p, struct snd_timer, device_list);
+ list_for_each_entry(timer, &snd_timer_list, device_list) {
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1088,14 +1077,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
snd_iprintf(buffer, " SLAVE");
snd_iprintf(buffer, "\n");
- list_for_each(q, &timer->open_list_head) {
- ti = list_entry(q, struct snd_timer_instance, open_list);
+ list_for_each_entry(ti, &timer->open_list_head, open_list)
snd_iprintf(buffer, " Client %s : %s\n",
ti->owner ? ti->owner : "unknown",
ti->flags & (SNDRV_TIMER_IFLG_START |
SNDRV_TIMER_IFLG_RUNNING)
? "running" : "stopped");
- }
}
mutex_unlock(&register_mutex);
}
@@ -1181,6 +1168,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
{
struct snd_timer_user *tu = timeri->callback_data;
struct snd_timer_tread r1;
+ unsigned long flags;
if (event >= SNDRV_TIMER_EVENT_START &&
event <= SNDRV_TIMER_EVENT_PAUSE)
@@ -1190,9 +1178,9 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
r1.event = event;
r1.tstamp = *tstamp;
r1.val = resolution;
- spin_lock(&tu->qlock);
+ spin_lock_irqsave(&tu->qlock, flags);
snd_timer_user_append_to_tqueue(tu, &r1);
- spin_unlock(&tu->qlock);
+ spin_unlock_irqrestore(&tu->qlock, flags);
kill_fasync(&tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
@@ -1213,8 +1201,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
spin_unlock(&tu->qlock);
return;
}
- if (tu->last_resolution != resolution || ticks > 0)
- getnstimeofday(&tstamp);
+ if (tu->last_resolution != resolution || ticks > 0) {
+ if (timer_tstamp_monotonic)
+ ktime_get_ts(&tstamp);
+ else
+ getnstimeofday(&tstamp);
+ }
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
@@ -1254,6 +1246,11 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
static int snd_timer_user_open(struct inode *inode, struct file *file)
{
struct snd_timer_user *tu;
+ int err;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
tu = kzalloc(sizeof(*tu), GFP_KERNEL);
if (tu == NULL)
@@ -1280,7 +1277,6 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
if (file->private_data) {
tu = file->private_data;
file->private_data = NULL;
- fasync_helper(-1, file, 0, &tu->fasync);
if (tu->timeri)
snd_timer_close(tu->timeri);
kfree(tu->queue);
@@ -1413,13 +1409,10 @@ static int snd_timer_user_ginfo(struct file *file,
struct list_head *p;
int err = 0;
- ginfo = kmalloc(sizeof(*ginfo), GFP_KERNEL);
- if (! ginfo)
- return -ENOMEM;
- if (copy_from_user(ginfo, _ginfo, sizeof(*ginfo))) {
- kfree(ginfo);
- return -EFAULT;
- }
+ ginfo = memdup_user(_ginfo, sizeof(*ginfo));
+ if (IS_ERR(ginfo))
+ return PTR_ERR(ginfo);
+
tid = ginfo->tid;
memset(ginfo, 0, sizeof(*ginfo));
ginfo->tid = tid;
@@ -1580,9 +1573,11 @@ static int snd_timer_user_info(struct file *file,
int err = 0;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
t = tu->timeri->timer;
- snd_assert(t != NULL, return -ENXIO);
+ if (!t)
+ return -EBADFD;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
@@ -1610,9 +1605,11 @@ static int snd_timer_user_params(struct file *file,
int err;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
t = tu->timeri->timer;
- snd_assert(t != NULL, return -ENXIO);
+ if (!t)
+ return -EBADFD;
if (copy_from_user(&params, _params, sizeof(params)))
return -EFAULT;
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
@@ -1706,7 +1703,8 @@ static int snd_timer_user_status(struct file *file,
struct snd_timer_status status;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
memset(&status, 0, sizeof(status));
status.tstamp = tu->tstamp;
status.resolution = snd_timer_resolution(tu->timeri);
@@ -1726,7 +1724,8 @@ static int snd_timer_user_start(struct file *file)
struct snd_timer_user *tu;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
@@ -1739,7 +1738,8 @@ static int snd_timer_user_stop(struct file *file)
struct snd_timer_user *tu;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
}
@@ -1749,7 +1749,8 @@ static int snd_timer_user_continue(struct file *file)
struct snd_timer_user *tu;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
tu->timeri->lost = 0;
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
}
@@ -1760,7 +1761,8 @@ static int snd_timer_user_pause(struct file *file)
struct snd_timer_user *tu;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (!tu->timeri)
+ return -EBADFD;
return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
}
@@ -1834,13 +1836,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
static int snd_timer_user_fasync(int fd, struct file * file, int on)
{
struct snd_timer_user *tu;
- int err;
tu = file->private_data;
- err = fasync_helper(fd, file, on, &tu->fasync);
- if (err < 0)
- return err;
- return 0;
+ return fasync_helper(fd, file, on, &tu->fasync);
}
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
@@ -1931,12 +1929,13 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
#define snd_timer_user_ioctl_compat NULL
#endif
-static struct file_operations snd_timer_f_ops =
+static const struct file_operations snd_timer_f_ops =
{
.owner = THIS_MODULE,
.read = snd_timer_user_read,
.open = snd_timer_user_open,
.release = snd_timer_user_release,
+ .llseek = no_llseek,
.poll = snd_timer_user_poll,
.unlocked_ioctl = snd_timer_user_ioctl,
.compat_ioctl = snd_timer_user_ioctl_compat,
@@ -1957,12 +1956,10 @@ static int __init alsa_timer_init(void)
#endif
if ((err = snd_timer_register_system()) < 0)
- snd_printk(KERN_ERR "unable to register system timer (%i)\n",
- err);
+ pr_err("ALSA: unable to register system timer (%i)\n", err);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
&snd_timer_f_ops, NULL, "timer")) < 0)
- snd_printk(KERN_ERR "unable to register timer device (%i)\n",
- err);
+ pr_err("ALSA: unable to register timer device (%i)\n", err);
snd_timer_proc_init();
return 0;
}
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 5512f5373c5..e05802ae6e1 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -40,9 +40,11 @@ static int snd_timer_user_info_compat(struct file *file,
struct snd_timer *t;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!tu->timeri))
+ return -ENXIO;
t = tu->timeri->timer;
- snd_assert(t != NULL, return -ENXIO);
+ if (snd_BUG_ON(!t))
+ return -ENXIO;
memset(&info, 0, sizeof(info));
info.card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
@@ -71,7 +73,8 @@ static int snd_timer_user_status_compat(struct file *file,
struct snd_timer_status status;
tu = file->private_data;
- snd_assert(tu->timeri != NULL, return -ENXIO);
+ if (snd_BUG_ON(!tu->timeri))
+ return -ENXIO;
memset(&status, 0, sizeof(status));
status.tstamp = tu->tstamp;
status.resolution = snd_timer_resolution(tu->timeri);
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
new file mode 100644
index 00000000000..6c58e6f73a0
--- /dev/null
+++ b/sound/core/vmaster.c
@@ -0,0 +1,486 @@
+/*
+ * Virtual master and slave controls
+ *
+ * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+/*
+ * a subset of information returned via ctl info callback
+ */
+struct link_ctl_info {
+ snd_ctl_elem_type_t type; /* value type */
+ int count; /* item count */
+ int min_val, max_val; /* min, max values */
+};
+
+/*
+ * link master - this contains a list of slave controls that are
+ * identical types, i.e. info returns the same value type and value
+ * ranges, but may have different number of counts.
+ *
+ * The master control is so far only mono volume/switch for simplicity.
+ * The same value will be applied to all slaves.
+ */
+struct link_master {
+ struct list_head slaves;
+ struct link_ctl_info info;
+ int val; /* the master value */
+ unsigned int tlv[4];
+ void (*hook)(void *private_data, int);
+ void *hook_private_data;
+};
+
+/*
+ * link slave - this contains a slave control element
+ *
+ * It fakes the control callbacsk with additional attenuation by the
+ * master control. A slave may have either one or two channels.
+ */
+
+struct link_slave {
+ struct list_head list;
+ struct link_master *master;
+ struct link_ctl_info info;
+ int vals[2]; /* current values */
+ unsigned int flags;
+ struct snd_kcontrol *kctl; /* original kcontrol pointer */
+ struct snd_kcontrol slave; /* the copy of original control entry */
+};
+
+static int slave_update(struct link_slave *slave)
+{
+ struct snd_ctl_elem_value *uctl;
+ int err, ch;
+
+ uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+ uctl->id = slave->slave.id;
+ err = slave->slave.get(&slave->slave, uctl);
+ for (ch = 0; ch < slave->info.count; ch++)
+ slave->vals[ch] = uctl->value.integer.value[ch];
+ kfree(uctl);
+ return 0;
+}
+
+/* get the slave ctl info and save the initial values */
+static int slave_init(struct link_slave *slave)
+{
+ struct snd_ctl_elem_info *uinfo;
+ int err;
+
+ if (slave->info.count) {
+ /* already initialized */
+ if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE)
+ return slave_update(slave);
+ return 0;
+ }
+
+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ if (!uinfo)
+ return -ENOMEM;
+ uinfo->id = slave->slave.id;
+ err = slave->slave.info(&slave->slave, uinfo);
+ if (err < 0) {
+ kfree(uinfo);
+ return err;
+ }
+ slave->info.type = uinfo->type;
+ slave->info.count = uinfo->count;
+ if (slave->info.count > 2 ||
+ (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+ slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+ pr_err("ALSA: vmaster: invalid slave element\n");
+ kfree(uinfo);
+ return -EINVAL;
+ }
+ slave->info.min_val = uinfo->value.integer.min;
+ slave->info.max_val = uinfo->value.integer.max;
+ kfree(uinfo);
+
+ return slave_update(slave);
+}
+
+/* initialize master volume */
+static int master_init(struct link_master *master)
+{
+ struct link_slave *slave;
+
+ if (master->info.count)
+ return 0; /* already initialized */
+
+ list_for_each_entry(slave, &master->slaves, list) {
+ int err = slave_init(slave);
+ if (err < 0)
+ return err;
+ master->info = slave->info;
+ master->info.count = 1; /* always mono */
+ /* set full volume as default (= no attenuation) */
+ master->val = master->info.max_val;
+ if (master->hook)
+ master->hook(master->hook_private_data, master->val);
+ return 1;
+ }
+ return -ENOENT;
+}
+
+static int slave_get_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] = slave->vals[ch];
+ return 0;
+}
+
+static int slave_put_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch, vol;
+
+ err = master_init(slave->master);
+ if (err < 0)
+ return err;
+
+ switch (slave->info.type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] &=
+ !!slave->master->val;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ for (ch = 0; ch < slave->info.count; ch++) {
+ /* max master volume is supposed to be 0 dB */
+ vol = ucontrol->value.integer.value[ch];
+ vol += slave->master->val - slave->master->info.max_val;
+ if (vol < slave->info.min_val)
+ vol = slave->info.min_val;
+ else if (vol > slave->info.max_val)
+ vol = slave->info.max_val;
+ ucontrol->value.integer.value[ch] = vol;
+ }
+ break;
+ }
+ return slave->slave.put(&slave->slave, ucontrol);
+}
+
+/*
+ * ctl callbacks for slaves
+ */
+static int slave_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave->slave.info(&slave->slave, uinfo);
+}
+
+static int slave_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave_get_val(slave, ucontrol);
+}
+
+static int slave_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ int err, ch, changed = 0;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++) {
+ if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+ changed = 1;
+ slave->vals[ch] = ucontrol->value.integer.value[ch];
+ }
+ }
+ if (!changed)
+ return 0;
+ err = slave_put_val(slave, ucontrol);
+ if (err < 0)
+ return err;
+ return 1;
+}
+
+static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ /* FIXME: this assumes that the max volume is 0 dB */
+ return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+}
+
+static void slave_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ if (slave->slave.private_free)
+ slave->slave.private_free(&slave->slave);
+ if (slave->master)
+ list_del(&slave->list);
+ kfree(slave);
+}
+
+/*
+ * Add a slave control to the group with the given master control
+ *
+ * All slaves must be the same type (returning the same information
+ * via info callback). The function doesn't check it, so it's your
+ * responsibility.
+ *
+ * Also, some additional limitations:
+ * - at most two channels
+ * - logarithmic volume control (dB level), no linear volume
+ * - master can only attenuate the volume, no gain
+ */
+int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
+ unsigned int flags)
+{
+ struct link_master *master_link = snd_kcontrol_chip(master);
+ struct link_slave *srec;
+
+ srec = kzalloc(sizeof(*srec) +
+ slave->count * sizeof(*slave->vd), GFP_KERNEL);
+ if (!srec)
+ return -ENOMEM;
+ srec->kctl = slave;
+ srec->slave = *slave;
+ memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+ srec->master = master_link;
+ srec->flags = flags;
+
+ /* override callbacks */
+ slave->info = slave_info;
+ slave->get = slave_get;
+ slave->put = slave_put;
+ if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+ slave->tlv.c = slave_tlv_cmd;
+ slave->private_data = srec;
+ slave->private_free = slave_free;
+
+ list_add_tail(&srec->list, &master_link->slaves);
+ return 0;
+}
+EXPORT_SYMBOL(_snd_ctl_add_slave);
+
+/*
+ * ctl callbacks for master controls
+ */
+static int master_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = master_init(master);
+ if (ret < 0)
+ return ret;
+ uinfo->type = master->info.type;
+ uinfo->count = master->info.count;
+ uinfo->value.integer.min = master->info.min_val;
+ uinfo->value.integer.max = master->info.max_val;
+ return 0;
+}
+
+static int master_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err = master_init(master);
+ if (err < 0)
+ return err;
+ ucontrol->value.integer.value[0] = master->val;
+ return 0;
+}
+
+static int sync_slaves(struct link_master *master, int old_val, int new_val)
+{
+ struct link_slave *slave;
+ struct snd_ctl_elem_value *uval;
+
+ uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+ if (!uval)
+ return -ENOMEM;
+ list_for_each_entry(slave, &master->slaves, list) {
+ master->val = old_val;
+ uval->id = slave->slave.id;
+ slave_get_val(slave, uval);
+ master->val = new_val;
+ slave_put_val(slave, uval);
+ }
+ kfree(uval);
+ return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err, new_val, old_val;
+ bool first_init;
+
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ first_init = err;
+ old_val = master->val;
+ new_val = ucontrol->value.integer.value[0];
+ if (new_val == old_val)
+ return 0;
+
+ err = sync_slaves(master, old_val, new_val);
+ if (err < 0)
+ return err;
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
+ return 1;
+}
+
+static void master_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ struct link_slave *slave, *n;
+
+ /* free all slave links and retore the original slave kctls */
+ list_for_each_entry_safe(slave, n, &master->slaves, list) {
+ struct snd_kcontrol *sctl = slave->kctl;
+ struct list_head olist = sctl->list;
+ memcpy(sctl, &slave->slave, sizeof(*sctl));
+ memcpy(sctl->vd, slave->slave.vd,
+ sctl->count * sizeof(*sctl->vd));
+ sctl->list = olist; /* keep the current linked-list */
+ kfree(slave);
+ }
+ kfree(master);
+}
+
+
+/**
+ * snd_ctl_make_virtual_master - Create a virtual master control
+ * @name: name string of the control element to create
+ * @tlv: optional TLV int array for dB information
+ *
+ * Creates a virtual master control with the given name string.
+ *
+ * After creating a vmaster element, you can add the slave controls
+ * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
+ *
+ * The optional argument @tlv can be used to specify the TLV information
+ * for dB scale of the master control. It should be a single element
+ * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
+ * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
+ *
+ * Return: The created control element, or %NULL for errors (ENOMEM).
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv)
+{
+ struct link_master *master;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new knew;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.name = name;
+ knew.info = master_info;
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return NULL;
+ INIT_LIST_HEAD(&master->slaves);
+
+ kctl = snd_ctl_new1(&knew, master);
+ if (!kctl) {
+ kfree(master);
+ return NULL;
+ }
+ /* override some callbacks */
+ kctl->info = master_info;
+ kctl->get = master_get;
+ kctl->put = master_put;
+ kctl->private_free = master_free;
+
+ /* additional (constant) TLV read */
+ if (tlv &&
+ (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
+ tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
+ tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ memcpy(master->tlv, tlv, sizeof(master->tlv));
+ kctl->tlv.p = master->tlv;
+ }
+
+ return kctl;
+}
+EXPORT_SYMBOL(snd_ctl_make_virtual_master);
+
+/**
+ * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
+ * @kcontrol: vmaster kctl element
+ * @hook: the hook function
+ * @private_data: the private_data pointer to be saved
+ *
+ * Adds the given hook to the vmaster control element so that it's called
+ * at each time when the value is changed.
+ *
+ * Return: Zero.
+ */
+int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
+ void (*hook)(void *private_data, int),
+ void *private_data)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ master->hook = hook;
+ master->hook_private_data = private_data;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
+
+/**
+ * snd_ctl_sync_vmaster - Sync the vmaster slaves and hook
+ * @kcontrol: vmaster kctl element
+ * @hook_only: sync only the hook
+ *
+ * Forcibly call the put callback of each slave and call the hook function
+ * to synchronize with the current value of the given vmaster element.
+ * NOP when NULL is passed to @kcontrol.
+ */
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
+{
+ struct link_master *master;
+ bool first_init = false;
+
+ if (!kcontrol)
+ return;
+ master = snd_kcontrol_chip(kcontrol);
+ if (!hook_only) {
+ int err = master_init(master);
+ if (err < 0)
+ return;
+ first_init = err;
+ err = sync_slaves(master, master->val, master->val);
+ if (err < 0)
+ return;
+ }
+
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);