diff options
Diffstat (limited to 'sound/core')
51 files changed, 1773 insertions, 886 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 8077f481d84f..48db44fa56fe 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -59,6 +59,9 @@ config SND_CORE_TEST config SND_COMPRESS_OFFLOAD tristate +config SND_COMPRESS_ACCEL + bool + config SND_JACK bool @@ -175,15 +178,6 @@ config SND_VERBOSE_PROCFS 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" - help - Say Y here to enable verbose log messages. These messages - will help to identify source file and position containing - printed messages. - - You don't need this unless you're debugging ALSA. - config SND_CTL_FAST_LOOKUP bool "Fast lookup of control elements" if EXPERT default y @@ -251,6 +245,16 @@ config SND_JACK_INJECTION_DEBUG Say Y if you are debugging via jack injection interface. If unsure select "N". +config SND_UTIMER + bool "Enable support for userspace-controlled virtual timers" + depends on SND_TIMER + help + Say Y to enable the support of userspace-controlled timers. These + timers are purely virtual, and they are supposed to be triggered + from userspace. They could be quite useful when synchronizing the + sound timing with userspace applications (for instance, when sending + data through snd-aloop). + config SND_VMASTER bool @@ -262,6 +266,5 @@ config SND_CTL_LED tristate select NEW_LEDS if SND_CTL_LED select LEDS_TRIGGERS if SND_CTL_LED - select LEDS_TRIGGER_AUDIO if SND_CTL_LED source "sound/core/seq/Kconfig" diff --git a/sound/core/Makefile b/sound/core/Makefile index b8aa886198ab..31a0623cc89d 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -24,18 +24,18 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o CFLAGS_pcm_lib.o := -I$(src) CFLAGS_pcm_native.o := -I$(src) -snd-pcm-dmaengine-objs := pcm_dmaengine.o +snd-pcm-dmaengine-y := pcm_dmaengine.o -snd-ctl-led-objs := control_led.o -snd-rawmidi-objs := rawmidi.o -snd-ump-objs := ump.o +snd-ctl-led-y := control_led.o +snd-rawmidi-y := rawmidi.o +snd-ump-y := ump.o snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o -snd-timer-objs := timer.o -snd-hrtimer-objs := hrtimer.o -snd-hwdep-objs := hwdep.o -snd-seq-device-objs := seq_device.o +snd-timer-y := timer.o +snd-hrtimer-y := hrtimer.o +snd-hwdep-y := hwdep.o +snd-seq-device-y := seq_device.o -snd-compress-objs := compress_offload.o +snd-compress-y := compress_offload.o obj-$(CONFIG_SND) += snd.o obj-$(CONFIG_SND_CTL_LED) += snd-ctl-led.o diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index f0008fa2d839..840bb9cfe789 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -24,6 +24,7 @@ #include <linux/types.h> #include <linux/uio.h> #include <linux/uaccess.h> +#include <linux/dma-buf.h> #include <linux/module.h> #include <linux/compat.h> #include <sound/core.h> @@ -54,6 +55,12 @@ struct snd_compr_file { static void error_delayed_work(struct work_struct *work); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) +static void snd_compr_task_free_all(struct snd_compr_stream *stream); +#else +static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { } +#endif + /* * a note on stream states used: * we use following states in the compressed core @@ -85,6 +92,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) dirn = SND_COMPRESS_PLAYBACK; else if ((f->f_flags & O_ACCMODE) == O_RDONLY) dirn = SND_COMPRESS_CAPTURE; + else if ((f->f_flags & O_ACCMODE) == O_RDWR) + dirn = SND_COMPRESS_ACCEL; else return -EINVAL; @@ -125,6 +134,9 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + INIT_LIST_HEAD(&runtime->tasks); +#endif data->stream.runtime = runtime; f->private_data = (void *)data; scoped_guard(mutex, &compr->lock) @@ -154,6 +166,8 @@ static int snd_compr_free(struct inode *inode, struct file *f) break; } + snd_compr_task_free_all(&data->stream); + data->stream.ops->free(&data->stream); if (!data->stream.runtime->dma_buffer_p) kfree(data->stream.runtime->buffer); @@ -226,6 +240,9 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) struct snd_compr_avail ioctl_avail; size_t avail; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; + avail = snd_compr_calc_avail(stream, &ioctl_avail); ioctl_avail.avail = avail; @@ -287,8 +304,10 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, return -EFAULT; stream = &data->stream; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; guard(mutex)(&stream->device->lock); - /* write is allowed when stream is running or has been steup */ + /* write is allowed when stream is running or has been setup */ switch (stream->runtime->state) { case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: @@ -336,6 +355,8 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, return -EFAULT; stream = &data->stream; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; guard(mutex)(&stream->device->lock); /* read is allowed when stream is running, paused, draining and setup @@ -385,6 +406,7 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) { struct snd_compr_file *data = f->private_data; struct snd_compr_stream *stream; + struct snd_compr_runtime *runtime; size_t avail; __poll_t retval = 0; @@ -392,10 +414,11 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) return EPOLLERR; stream = &data->stream; + runtime = stream->runtime; guard(mutex)(&stream->device->lock); - switch (stream->runtime->state) { + switch (runtime->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_XRUN: return snd_compr_get_poll(stream) | EPOLLERR; @@ -403,23 +426,37 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) break; } - poll_wait(f, &stream->runtime->sleep, wait); + poll_wait(f, &runtime->sleep, wait); + +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + if (stream->direction == SND_COMPRESS_ACCEL) { + struct snd_compr_task_runtime *task; + if (runtime->fragments > runtime->active_tasks) + retval |= EPOLLOUT | EPOLLWRNORM; + task = list_first_entry_or_null(&runtime->tasks, + struct snd_compr_task_runtime, + list); + if (task && task->state == SND_COMPRESS_TASK_STATE_FINISHED) + retval |= EPOLLIN | EPOLLRDNORM; + return retval; + } +#endif 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) { + switch (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; + 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) + if (avail >= runtime->fragment_size) retval = snd_compr_get_poll(stream); break; default: @@ -521,6 +558,9 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, unsigned int buffer_size; void *buffer = NULL; + if (stream->direction == SND_COMPRESS_ACCEL) + goto params; + buffer_size = params->buffer.fragment_size * params->buffer.fragments; if (stream->ops->copy) { buffer = NULL; @@ -543,18 +583,30 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 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; +params: + stream->runtime->fragment_size = params->buffer.fragment_size; + stream->runtime->fragments = params->buffer.fragments; return 0; } -static int snd_compress_check_input(struct snd_compr_params *params) +static int +snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_params *params) { + u32 max_fragments; + /* first let's check the buffer parameter's */ - if (params->buffer.fragment_size == 0 || - params->buffer.fragments > U32_MAX / params->buffer.fragment_size || + if (params->buffer.fragment_size == 0) + return -EINVAL; + + if (stream->direction == SND_COMPRESS_ACCEL) + max_fragments = 64; /* safe value */ + else + max_fragments = U32_MAX / params->buffer.fragment_size; + + if (params->buffer.fragments > max_fragments || params->buffer.fragments == 0) return -EINVAL; @@ -581,9 +633,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) */ params = memdup_user((void __user *)arg, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(no_free_ptr(params)); + return PTR_ERR(params); - retval = snd_compress_check_input(params); + retval = snd_compress_check_input(stream, params); if (retval) return retval; @@ -939,6 +991,273 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) return snd_compress_wait_for_drain(stream); } +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + +static struct snd_compr_task_runtime * +snd_compr_find_task(struct snd_compr_stream *stream, __u64 seqno) +{ + struct snd_compr_task_runtime *task; + + list_for_each_entry(task, &stream->runtime->tasks, list) { + if (task->seqno == seqno) + return task; + } + return NULL; +} + +static void snd_compr_task_free(struct snd_compr_task_runtime *task) +{ + if (task->output) + dma_buf_put(task->output); + if (task->input) + dma_buf_put(task->input); + kfree(task); +} + +static u64 snd_compr_seqno_next(struct snd_compr_stream *stream) +{ + u64 seqno = ++stream->runtime->task_seqno; + if (seqno == 0) + seqno = ++stream->runtime->task_seqno; + return seqno; +} + +static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask) +{ + struct snd_compr_task_runtime *task; + int retval, fd_i, fd_o; + + if (stream->runtime->total_tasks >= stream->runtime->fragments) + return -EBUSY; + if (utask->origin_seqno != 0 || utask->input_size != 0) + return -EINVAL; + task = kzalloc(sizeof(*task), GFP_KERNEL); + if (task == NULL) + return -ENOMEM; + task->seqno = utask->seqno = snd_compr_seqno_next(stream); + task->input_size = utask->input_size; + retval = stream->ops->task_create(stream, task); + if (retval < 0) + goto cleanup; + /* similar functionality as in dma_buf_fd(), but ensure that both + file descriptors are allocated before fd_install() */ + if (!task->input || !task->input->file || !task->output || !task->output->file) { + retval = -EINVAL; + goto cleanup; + } + fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC); + if (fd_i < 0) + goto cleanup; + fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC); + if (fd_o < 0) { + put_unused_fd(fd_i); + goto cleanup; + } + /* keep dmabuf reference until freed with task free ioctl */ + get_dma_buf(task->input); + get_dma_buf(task->output); + fd_install(fd_i, task->input->file); + fd_install(fd_o, task->output->file); + utask->input_fd = fd_i; + utask->output_fd = fd_o; + list_add_tail(&task->list, &stream->runtime->tasks); + stream->runtime->total_tasks++; + return 0; +cleanup: + snd_compr_task_free(task); + return retval; +} + +static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task *task __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + task = memdup_user((void __user *)arg, sizeof(*task)); + if (IS_ERR(task)) + return PTR_ERR(task); + retval = snd_compr_task_new(stream, task); + if (retval >= 0) + if (copy_to_user((void __user *)arg, task, sizeof(*task))) + retval = -EFAULT; + return retval; +} + +static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task, + struct snd_compr_task *utask) +{ + if (task == NULL) + return -EINVAL; + if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED) + return -EBUSY; + if (utask->input_size > task->input->size) + return -EINVAL; + task->flags = utask->flags; + task->input_size = utask->input_size; + task->state = SND_COMPRESS_TASK_STATE_IDLE; + return 0; +} + +static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_compr_task *utask) +{ + struct snd_compr_task_runtime *task; + int retval; + + if (utask->origin_seqno > 0) { + task = snd_compr_find_task(stream, utask->origin_seqno); + retval = snd_compr_task_start_prepare(task, utask); + if (retval < 0) + return retval; + task->seqno = utask->seqno = snd_compr_seqno_next(stream); + utask->origin_seqno = 0; + list_move_tail(&task->list, &stream->runtime->tasks); + } else { + task = snd_compr_find_task(stream, utask->seqno); + if (task && task->state != SND_COMPRESS_TASK_STATE_IDLE) + return -EBUSY; + retval = snd_compr_task_start_prepare(task, utask); + if (retval < 0) + return retval; + } + retval = stream->ops->task_start(stream, task); + if (retval >= 0) { + task->state = SND_COMPRESS_TASK_STATE_ACTIVE; + stream->runtime->active_tasks++; + } + return retval; +} + +static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task *task __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + task = memdup_user((void __user *)arg, sizeof(*task)); + if (IS_ERR(task)) + return PTR_ERR(task); + retval = snd_compr_task_start(stream, task); + if (retval >= 0) + if (copy_to_user((void __user *)arg, task, sizeof(*task))) + retval = -EFAULT; + return retval; +} + +static void snd_compr_task_stop_one(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + if (task->state != SND_COMPRESS_TASK_STATE_ACTIVE) + return; + stream->ops->task_stop(stream, task); + if (!snd_BUG_ON(stream->runtime->active_tasks == 0)) + stream->runtime->active_tasks--; + list_move_tail(&task->list, &stream->runtime->tasks); + task->state = SND_COMPRESS_TASK_STATE_IDLE; +} + +static void snd_compr_task_free_one(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + snd_compr_task_stop_one(stream, task); + stream->ops->task_free(stream, task); + list_del(&task->list); + snd_compr_task_free(task); + stream->runtime->total_tasks--; +} + +static void snd_compr_task_free_all(struct snd_compr_stream *stream) +{ + struct snd_compr_task_runtime *task, *temp; + + list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list) + snd_compr_task_free_one(stream, task); +} + +typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task); + +static int snd_compr_task_seq(struct snd_compr_stream *stream, unsigned long arg, + snd_compr_seq_func_t fcn) +{ + struct snd_compr_task_runtime *task, *temp; + __u64 seqno; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + retval = copy_from_user(&seqno, (__u64 __user *)arg, sizeof(seqno)); + if (retval) + return -EFAULT; + retval = 0; + if (seqno == 0) { + list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list) + fcn(stream, task); + } else { + task = snd_compr_find_task(stream, seqno); + if (task == NULL) { + retval = -EINVAL; + } else { + fcn(stream, task); + } + } + return retval; +} + +static int snd_compr_task_status(struct snd_compr_stream *stream, + struct snd_compr_task_status *status) +{ + struct snd_compr_task_runtime *task; + + task = snd_compr_find_task(stream, status->seqno); + if (task == NULL) + return -EINVAL; + status->input_size = task->input_size; + status->output_size = task->output_size; + status->state = task->state; + return 0; +} + +static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task_status *status __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + status = memdup_user((void __user *)arg, sizeof(*status)); + if (IS_ERR(status)) + return PTR_ERR(status); + retval = snd_compr_task_status(stream, status); + if (retval >= 0) + if (copy_to_user((void __user *)arg, status, sizeof(*status))) + retval = -EFAULT; + return retval; +} + +/** + * snd_compr_task_finished: Notify that the task was finished + * @stream: pointer to stream + * @task: runtime task structure + * + * Set the finished task state and notify waiters. + */ +void snd_compr_task_finished(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + guard(mutex)(&stream->device->lock); + if (!snd_BUG_ON(stream->runtime->active_tasks == 0)) + stream->runtime->active_tasks--; + task->state = SND_COMPRESS_TASK_STATE_FINISHED; + wake_up(&stream->runtime->sleep); +} +EXPORT_SYMBOL_GPL(snd_compr_task_finished); + +MODULE_IMPORT_NS("DMA_BUF"); +#endif /* CONFIG_SND_COMPRESS_ACCEL */ + static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { struct snd_compr_file *data = f->private_data; @@ -968,6 +1287,27 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) return snd_compr_set_metadata(stream, arg); case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): return snd_compr_get_metadata(stream, arg); + } + + if (stream->direction == SND_COMPRESS_ACCEL) { +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_COMPRESS_TASK_CREATE): + return snd_compr_task_create(stream, arg); + case _IOC_NR(SNDRV_COMPRESS_TASK_FREE): + return snd_compr_task_seq(stream, arg, snd_compr_task_free_one); + case _IOC_NR(SNDRV_COMPRESS_TASK_START): + return snd_compr_task_start_ioctl(stream, arg); + case _IOC_NR(SNDRV_COMPRESS_TASK_STOP): + return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one); + case _IOC_NR(SNDRV_COMPRESS_TASK_STATUS): + return snd_compr_task_status_ioctl(stream, arg); + } +#endif + return -ENOTTY; + } + + switch (_IOC_NR(cmd)) { case _IOC_NR(SNDRV_COMPRESS_TSTAMP): return snd_compr_tstamp(stream, arg); case _IOC_NR(SNDRV_COMPRESS_AVAIL): @@ -1140,6 +1480,11 @@ int snd_compress_new(struct snd_card *card, int device, }; int ret; +#if !IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + if (snd_BUG_ON(dirn == SND_COMPRESS_ACCEL)) + return -EINVAL; +#endif + compr->card = card; compr->device = device; compr->direction = dirn; diff --git a/sound/core/control.c b/sound/core/control.c index fb0c60044f7b..0ddade871b52 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -79,7 +79,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) ctl->preferred_subdevice[i] = -1; ctl->pid = get_pid(task_pid(current)); file->private_data = ctl; - scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock) + scoped_guard(write_lock_irqsave, &card->controls_rwlock) list_add_tail(&ctl->list, &card->ctl_files); snd_card_unref(card); return 0; @@ -117,7 +117,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file) file->private_data = NULL; card = ctl->card; - scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock) + scoped_guard(write_lock_irqsave, &card->controls_rwlock) list_del(&ctl->list); scoped_guard(rwsem_write, &card->controls_rwsem) { @@ -157,7 +157,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, if (card->shutdown) return; - guard(read_lock_irqsave)(&card->ctl_files_rwlock); + guard(read_lock_irqsave)(&card->controls_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; #endif @@ -237,11 +237,11 @@ static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, if (!*kctl) return -ENOMEM; + (*kctl)->count = count; for (idx = 0; idx < count; idx++) { (*kctl)->vd[idx].access = access; (*kctl)->vd[idx].owner = file; } - (*kctl)->count = count; return 0; } @@ -470,7 +470,7 @@ static int __snd_ctl_add_replace(struct snd_card *card, if (id.index > UINT_MAX - kcontrol->count) return -EINVAL; - old = snd_ctl_find_id_locked(card, &id); + old = snd_ctl_find_id(card, &id); if (!old) { if (mode == CTL_REPLACE) return -EINVAL; @@ -491,10 +491,12 @@ static int __snd_ctl_add_replace(struct snd_card *card, if (snd_ctl_find_hole(card, kcontrol->count) < 0) return -ENOMEM; - list_add_tail(&kcontrol->list, &card->controls); - card->controls_count += kcontrol->count; - kcontrol->id.numid = card->last_numid + 1; - card->last_numid += kcontrol->count; + scoped_guard(write_lock_irq, &card->controls_rwlock) { + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count += kcontrol->count; + kcontrol->id.numid = card->last_numid + 1; + card->last_numid += kcontrol->count; + } add_hash_entries(card, kcontrol); @@ -579,12 +581,15 @@ static int __snd_ctl_remove(struct snd_card *card, if (snd_BUG_ON(!card || !kcontrol)) return -EINVAL; - list_del(&kcontrol->list); if (remove_hash) remove_hash_entries(card, kcontrol); - card->controls_count -= kcontrol->count; + scoped_guard(write_lock_irq, &card->controls_rwlock) { + list_del(&kcontrol->list); + card->controls_count -= kcontrol->count; + } + for (idx = 0; idx < kcontrol->count; idx++) snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx); snd_ctl_free_one(kcontrol); @@ -604,6 +609,7 @@ static inline int snd_ctl_remove_locked(struct snd_card *card, * * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). + * Passing NULL to @kcontrol argument is allowed as noop. * * Return: 0 if successful, or a negative error code on failure. * @@ -611,6 +617,8 @@ static inline int snd_ctl_remove_locked(struct snd_card *card, */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { + if (!kcontrol) + return 0; guard(rwsem_write)(&card->controls_rwsem); return snd_ctl_remove_locked(card, kcontrol); } @@ -631,7 +639,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) struct snd_kcontrol *kctl; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); + kctl = snd_ctl_find_id(card, id); if (kctl == NULL) return -ENOENT; return snd_ctl_remove_locked(card, kctl); @@ -656,7 +664,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, int idx; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); + kctl = snd_ctl_find_id(card, id); if (kctl == NULL) return -ENOENT; if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) @@ -688,7 +696,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int ret; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); + kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { ret = -ENOENT; goto unlock; @@ -742,7 +750,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, int saved_numid; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, src_id); + kctl = snd_ctl_find_id(card, src_id); if (kctl == NULL) return -ENOENT; saved_numid = kctl->id.numid; @@ -784,6 +792,7 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid) { struct snd_kcontrol *kctl; + guard(read_lock_irqsave)(&card->controls_rwlock); list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) return kctl; @@ -793,72 +802,51 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid) #endif /* !CONFIG_SND_CTL_FAST_LOOKUP */ /** - * snd_ctl_find_numid_locked - find the control instance with the given number-id + * snd_ctl_find_numid - find the control instance with the given number-id * @card: the card instance * @numid: the number-id to search * * Finds the control instance with the given number-id from the card. * - * 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. + * + * Note that this function takes card->controls_rwlock lock internally. */ -struct snd_kcontrol * -snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid) +struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, + unsigned int numid) { if (snd_BUG_ON(!card || !numid)) return NULL; - lockdep_assert_held(&card->controls_rwsem); + #ifdef CONFIG_SND_CTL_FAST_LOOKUP return xa_load(&card->ctl_numids, numid); #else return snd_ctl_find_numid_slow(card, numid); #endif } -EXPORT_SYMBOL(snd_ctl_find_numid_locked); - -/** - * snd_ctl_find_numid - find the control instance with the given number-id - * @card: the card instance - * @numid: the number-id to search - * - * Finds the control instance with the given number-id from the card. - * - * Return: The pointer of the instance if found, or %NULL if not. - * - * Note that this function takes card->controls_rwsem lock internally. - */ -struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, - unsigned int numid) -{ - guard(rwsem_read)(&card->controls_rwsem); - return snd_ctl_find_numid_locked(card, numid); -} EXPORT_SYMBOL(snd_ctl_find_numid); /** - * snd_ctl_find_id_locked - find the control instance with the given id + * snd_ctl_find_id - find the control instance with the given id * @card: the card instance * @id: the id to search * * Finds the control instance with the given id from the card. * - * 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. + * + * Note that this function takes card->controls_rwlock lock internally. */ -struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card, - const struct snd_ctl_elem_id *id) +struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, + const struct snd_ctl_elem_id *id) { struct snd_kcontrol *kctl; if (snd_BUG_ON(!card || !id)) return NULL; - lockdep_assert_held(&card->controls_rwsem); + if (id->numid != 0) - return snd_ctl_find_numid_locked(card, id->numid); + return snd_ctl_find_numid(card, id->numid); #ifdef CONFIG_SND_CTL_FAST_LOOKUP kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id)); if (kctl && elem_id_matches(kctl, id)) @@ -867,31 +855,13 @@ struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card, return NULL; /* we can rely on only hash table */ #endif /* no matching in hash table - try all as the last resort */ + guard(read_lock_irqsave)(&card->controls_rwlock); list_for_each_entry(kctl, &card->controls, list) if (elem_id_matches(kctl, id)) return kctl; return NULL; } -EXPORT_SYMBOL(snd_ctl_find_id_locked); - -/** - * snd_ctl_find_id - find the control instance with the given id - * @card: the card instance - * @id: the id to search - * - * Finds the control instance with the given id from the card. - * - * Return: The pointer of the instance if found, or %NULL if not. - * - * Note that this function takes card->controls_rwsem lock internally. - */ -struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, - const struct snd_ctl_elem_id *id) -{ - guard(rwsem_read)(&card->controls_rwsem); - return snd_ctl_find_id_locked(card, id); -} EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, @@ -1164,10 +1134,7 @@ static int __snd_ctl_elem_info(struct snd_card *card, #ifdef CONFIG_SND_DEBUG info->access = 0; #endif - result = snd_power_ref_and_wait(card); - if (!result) - result = kctl->info(kctl, info); - snd_power_unref(card); + result = kctl->info(kctl, info); if (result >= 0) { snd_BUG_ON(info->access); index_offset = snd_ctl_get_ioff(kctl, &info->id); @@ -1196,7 +1163,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, struct snd_kcontrol *kctl; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, &info->id); + kctl = snd_ctl_find_id(card, &info->id); if (!kctl) return -ENOENT; return __snd_ctl_elem_info(card, kctl, info, ctl); @@ -1205,12 +1172,17 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, struct snd_ctl_elem_info __user *_info) { + struct snd_card *card = ctl->card; struct snd_ctl_elem_info info; int result; if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; + result = snd_power_ref_and_wait(card); + if (result) + return result; result = snd_ctl_elem_info(ctl, &info); + snd_power_unref(card); if (result < 0) return result; /* drop internal access flags */ @@ -1232,7 +1204,7 @@ static int snd_ctl_elem_read(struct snd_card *card, int ret; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, &control->id); + kctl = snd_ctl_find_id(card, &control->id); if (!kctl) return -ENOENT; @@ -1254,10 +1226,7 @@ static int snd_ctl_elem_read(struct snd_card *card, if (!snd_ctl_skip_validation(&info)) fill_remaining_elem_value(control, &info, pattern); - ret = snd_power_ref_and_wait(card); - if (!ret) - ret = kctl->get(kctl, control); - snd_power_unref(card); + ret = kctl->get(kctl, control); if (ret < 0) return ret; if (!snd_ctl_skip_validation(&info) && @@ -1280,9 +1249,13 @@ static int snd_ctl_elem_read_user(struct snd_card *card, control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) - return PTR_ERR(no_free_ptr(control)); + return PTR_ERR(control); + result = snd_power_ref_and_wait(card); + if (result) + return result; result = snd_ctl_elem_read(card, control); + snd_power_unref(card); if (result < 0) return result; @@ -1297,10 +1270,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsigned int index_offset; - int result; + int result = 0; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, &control->id); + kctl = snd_ctl_find_id(card, &control->id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; @@ -1315,9 +1288,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, } snd_ctl_build_ioff(&control->id, kctl, index_offset); - result = snd_power_ref_and_wait(card); /* validate input values */ - if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) { + if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION)) { struct snd_ctl_elem_info info; memset(&info, 0, sizeof(info)); @@ -1329,7 +1301,6 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, } if (!result) result = kctl->put(kctl, control); - snd_power_unref(card); if (result < 0) { up_write(&card->controls_rwsem); return result; @@ -1355,10 +1326,14 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) - return PTR_ERR(no_free_ptr(control)); + return PTR_ERR(control); card = file->card; + result = snd_power_ref_and_wait(card); + if (result < 0) + return result; result = snd_ctl_elem_write(card, file, control); + snd_power_unref(card); if (result < 0) return result; @@ -1378,7 +1353,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file, if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, &id); + kctl = snd_ctl_find_id(card, &id); if (!kctl) return -ENOENT; vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; @@ -1399,7 +1374,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, &id); + kctl = snd_ctl_find_id(card, &id); if (!kctl) return -ENOENT; vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; @@ -1480,12 +1455,16 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int change; + int err, change; struct user_element *ue = kcontrol->private_data; unsigned int size = ue->elem_data_size; char *dst = ue->elem_data + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; + err = sanity_check_input_values(ue->card, ucontrol, &ue->info, false); + if (err < 0) + return err; + change = memcmp(&ucontrol->value, dst, size) != 0; if (change) memcpy(dst, &ucontrol->value, size); @@ -1662,6 +1641,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, count = info->owner; if (count == 0) count = 1; + if (count > MAX_CONTROL_COUNT) + return -EINVAL; /* Arrange access permissions if needed. */ access = info->access; @@ -1823,7 +1804,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND}, }; struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)]; - int i, ret; + int i; /* Check support of the request for this element. */ for (i = 0; i < ARRAY_SIZE(pairs); ++i) { @@ -1841,11 +1822,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, vd->owner != NULL && vd->owner != file) return -EPERM; - ret = snd_power_ref_and_wait(file->card); - if (!ret) - ret = kctl->tlv.c(kctl, op_flag, size, buf); - snd_power_unref(file->card); - return ret; + return kctl->tlv.c(kctl, op_flag, size, buf); } static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id, @@ -1896,7 +1873,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, container_size = header.length; container = buf->tlv; - kctl = snd_ctl_find_numid_locked(file->card, header.numid); + kctl = snd_ctl_find_numid(file->card, header.numid); if (kctl == NULL) return -ENOENT; @@ -1958,16 +1935,28 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: return snd_ctl_subscribe_events(ctl, ip); case SNDRV_CTL_IOCTL_TLV_READ: - scoped_guard(rwsem_read, &ctl->card->controls_rwsem) + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; + scoped_guard(rwsem_read, &card->controls_rwsem) err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ); + snd_power_unref(card); return err; case SNDRV_CTL_IOCTL_TLV_WRITE: - scoped_guard(rwsem_write, &ctl->card->controls_rwsem) + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; + scoped_guard(rwsem_write, &card->controls_rwsem) err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE); + snd_power_unref(card); return err; case SNDRV_CTL_IOCTL_TLV_COMMAND: - scoped_guard(rwsem_write, &ctl->card->controls_rwsem) + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; + scoped_guard(rwsem_write, &card->controls_rwsem) err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD); + snd_power_unref(card); return err; case SNDRV_CTL_IOCTL_POWER: return -ENOPROTOOPT; @@ -2171,7 +2160,7 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type) struct snd_ctl_file *kctl; int subdevice = -1; - guard(read_lock_irqsave)(&card->ctl_files_rwlock); + guard(read_lock_irqsave)(&card->controls_rwlock); list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == task_pid(current)) { subdevice = kctl->preferred_subdevice[type]; @@ -2280,7 +2269,6 @@ static const struct file_operations snd_ctl_f_ops = .read = snd_ctl_read, .open = snd_ctl_open, .release = snd_ctl_release, - .llseek = no_llseek, .poll = snd_ctl_poll, .unlocked_ioctl = snd_ctl_ioctl, .compat_ioctl = snd_ctl_ioctl_compat, @@ -2321,7 +2309,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) struct snd_card *card = device->device_data; struct snd_ctl_file *ctl; - scoped_guard(read_lock_irqsave, &card->ctl_files_rwlock) { + scoped_guard(read_lock_irqsave, &card->controls_rwlock) { list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR); diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 934bb945e702..6459809ed364 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -79,6 +79,7 @@ struct snd_ctl_elem_info32 { static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, struct snd_ctl_elem_info32 __user *data32) { + struct snd_card *card = ctl->card; struct snd_ctl_elem_info *data __free(kfree) = NULL; int err; @@ -95,7 +96,11 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) return -EFAULT; + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; err = snd_ctl_elem_info(ctl, data); + snd_power_unref(card); if (err < 0) return err; /* restore info to 32bit */ @@ -168,17 +173,14 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, int err; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); + kctl = snd_ctl_find_id(card, id); if (!kctl) return -ENOENT; info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) return -ENOMEM; info->id = *id; - err = snd_power_ref_and_wait(card); - if (!err) - err = kctl->info(kctl, info); - snd_power_unref(card); + err = kctl->info(kctl, info); if (err >= 0) { err = info->type; *countp = info->count; @@ -275,8 +277,8 @@ static int copy_ctl_value_to_user(void __user *userdata, return 0; } -static int ctl_elem_read_user(struct snd_card *card, - void __user *userdata, void __user *valuep) +static int __ctl_elem_read_user(struct snd_card *card, + void __user *userdata, void __user *valuep) { struct snd_ctl_elem_value *data __free(kfree) = NULL; int err, type, count; @@ -296,8 +298,21 @@ static int ctl_elem_read_user(struct snd_card *card, return copy_ctl_value_to_user(userdata, valuep, data, type, count); } -static int ctl_elem_write_user(struct snd_ctl_file *file, - void __user *userdata, void __user *valuep) +static int ctl_elem_read_user(struct snd_card *card, + void __user *userdata, void __user *valuep) +{ + int err; + + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; + err = __ctl_elem_read_user(card, userdata, valuep); + snd_power_unref(card); + return err; +} + +static int __ctl_elem_write_user(struct snd_ctl_file *file, + void __user *userdata, void __user *valuep) { struct snd_ctl_elem_value *data __free(kfree) = NULL; struct snd_card *card = file->card; @@ -318,6 +333,20 @@ static int ctl_elem_write_user(struct snd_ctl_file *file, return copy_ctl_value_to_user(userdata, valuep, data, type, count); } +static int ctl_elem_write_user(struct snd_ctl_file *file, + void __user *userdata, void __user *valuep) +{ + struct snd_card *card = file->card; + int err; + + err = snd_power_ref_and_wait(card); + if (err < 0) + return err; + err = __ctl_elem_write_user(file, userdata, valuep); + snd_power_unref(card); + return err; +} + static int snd_ctl_elem_read_user_compat(struct snd_card *card, struct snd_ctl_elem_value32 __user *data32) { diff --git a/sound/core/control_led.c b/sound/core/control_led.c index 3d37e9fa7b9c..e33dfcf863cf 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -53,6 +53,7 @@ struct snd_ctl_led_ctl { static DEFINE_MUTEX(snd_ctl_led_mutex); static bool snd_ctl_led_card_valid[SNDRV_CARDS]; +static struct led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS]; static struct snd_ctl_led snd_ctl_leds[MAX_LED] = { { .name = "speaker", @@ -174,8 +175,11 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access, case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break; case MODE_FOLLOW_MUTE: /* noop */ break; } - if (route >= 0) - ledtrig_audio_set(led->trigger_type, route ? LED_OFF : LED_ON); + if (route >= 0) { + struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type]; + + led_trigger_event(trig, route ? LED_OFF : LED_ON); + } } static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff) @@ -250,7 +254,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, if (!card) return -ENXIO; guard(rwsem_write)(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); + kctl = snd_ctl_find_id(card, id); if (!kctl) return -ENOENT; ioff = snd_ctl_get_ioff(kctl, id); @@ -285,25 +289,22 @@ static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl) static void snd_ctl_led_clean(struct snd_card *card) { unsigned int group; + struct snd_ctl_led_ctl *lctl, *_lctl; struct snd_ctl_led *led; - struct snd_ctl_led_ctl *lctl; for (group = 0; group < MAX_LED; group++) { led = &snd_ctl_leds[group]; -repeat: - list_for_each_entry(lctl, &led->controls, list) - if (!card || lctl->card == card) { + list_for_each_entry_safe(lctl, _lctl, &led->controls, list) + if (!card || lctl->card == card) snd_ctl_led_ctl_destroy(lctl); - goto repeat; - } } } static int snd_ctl_led_reset(int card_number, unsigned int group) { struct snd_card *card __free(snd_card_unref) = NULL; + struct snd_ctl_led_ctl *lctl, *_lctl; struct snd_ctl_led *led; - struct snd_ctl_led_ctl *lctl; struct snd_kcontrol_volatile *vd; bool change = false; @@ -315,14 +316,12 @@ static int snd_ctl_led_reset(int card_number, unsigned int group) if (!snd_ctl_led_card_valid[card_number]) return -ENXIO; led = &snd_ctl_leds[group]; -repeat: - list_for_each_entry(lctl, &led->controls, list) + list_for_each_entry_safe(lctl, _lctl, &led->controls, list) if (lctl->card == card) { vd = &lctl->kctl->vd[lctl->index_offset]; vd->access &= ~group_to_access(group); snd_ctl_led_ctl_destroy(lctl); change = true; - goto repeat; } } if (change) @@ -425,8 +424,9 @@ static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev); + struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type]; - return sysfs_emit(buf, "%u\n", ledtrig_audio_get(led->trigger_type)); + return sysfs_emit(buf, "%u\n", led_trigger_get_brightness(trig)); } static DEVICE_ATTR_RW(mode); @@ -668,16 +668,22 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card) goto cerr; led->cards[card->number] = led_card; snprintf(link_name, sizeof(link_name), "led-%s", led->name); - WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name), - "can't create symlink to controlC%i device\n", card->number); - WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"), - "can't create symlink to card%i\n", card->number); + if (sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, + link_name)) + dev_err(card->dev, + "%s: can't create symlink to controlC%i device\n", + __func__, card->number); + if (sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, + "card")) + dev_err(card->dev, + "%s: can't create symlink to card%i\n", + __func__, card->number); continue; cerr: put_device(&led_card->dev); cerr2: - printk(KERN_ERR "snd_ctl_led: unable to add card%d", card->number); + dev_err(card->dev, "snd_ctl_led: unable to add card%d", card->number); } } @@ -716,6 +722,9 @@ static int __init snd_ctl_led_init(void) struct snd_ctl_led *led; unsigned int group; + led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]); + device_initialize(&snd_ctl_led_dev); snd_ctl_led_dev.class = &sound_class; snd_ctl_led_dev.release = snd_ctl_led_dev_release; @@ -768,7 +777,13 @@ static void __exit snd_ctl_led_exit(void) } device_unregister(&snd_ctl_led_dev); snd_ctl_led_clean(NULL); + + led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]); } module_init(snd_ctl_led_init) module_exit(snd_ctl_led_exit) + +MODULE_ALIAS("ledtrig:audio-mute"); +MODULE_ALIAS("ledtrig:audio-micmute"); diff --git a/sound/core/init.c b/sound/core/init.c index 4ed5037d8693..114fb87de990 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -50,7 +50,7 @@ MODULE_PARM_DESC(slots, "Module names assigned to the slots."); static int module_slot_match(struct module *module, int idx) { int match = 1; -#ifdef MODULE +#ifdef CONFIG_MODULES const char *s1, *s2; if (!module || !*module->name || !slots[idx]) @@ -77,7 +77,7 @@ static int module_slot_match(struct module *module, int idx) if (!c1) break; } -#endif /* MODULE */ +#endif /* CONFIG_MODULES */ return match; } @@ -311,13 +311,11 @@ static int snd_card_init(struct snd_card *card, struct device *parent, } card->dev = parent; card->number = idx; -#ifdef MODULE - WARN_ON(!module); + WARN_ON(IS_MODULE(CONFIG_SND) && !module); card->module = module; -#endif INIT_LIST_HEAD(&card->devices); init_rwsem(&card->controls_rwsem); - rwlock_init(&card->ctl_files_rwlock); + rwlock_init(&card->controls_rwlock); INIT_LIST_HEAD(&card->controls); INIT_LIST_HEAD(&card->ctl_files); #ifdef CONFIG_SND_CTL_FAST_LOOKUP @@ -516,6 +514,14 @@ void snd_card_disconnect(struct snd_card *card) } } +#ifdef CONFIG_PM + /* wake up sleepers here before other callbacks for avoiding potential + * deadlocks with other locks (e.g. in kctls); + * then this notifies the shutdown and sleepers would abort immediately + */ + wake_up_all(&card->power_sleep); +#endif + /* notify all connected devices about disconnection */ /* at this point, they cannot respond to any calls except release() */ @@ -531,6 +537,11 @@ void snd_card_disconnect(struct snd_card *card) synchronize_irq(card->sync_irq); snd_info_card_disconnect(card); +#ifdef CONFIG_SND_DEBUG + debugfs_remove(card->debugfs_root); + card->debugfs_root = NULL; +#endif + if (card->registered) { device_del(&card->card_dev); card->registered = false; @@ -542,10 +553,7 @@ void snd_card_disconnect(struct snd_card *card) clear_bit(card->number, snd_cards_lock); } -#ifdef CONFIG_PM - wake_up(&card->power_sleep); snd_power_sync_ref(card); -#endif } EXPORT_SYMBOL(snd_card_disconnect); @@ -583,10 +591,6 @@ static int snd_card_do_free(struct snd_card *card) dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ } -#ifdef CONFIG_SND_DEBUG - debugfs_remove(card->debugfs_root); - card->debugfs_root = NULL; -#endif if (card->release_completion) complete(card->release_completion); if (!card->managed) @@ -650,13 +654,19 @@ void snd_card_free(struct snd_card *card) } EXPORT_SYMBOL(snd_card_free); +/* check, if the character is in the valid ASCII range */ +static inline bool safe_ascii_char(char c) +{ + return isascii(c) && isalnum(c); +} + /* retrieve the last word of shortname or longname */ static const char *retrieve_id_from_card_name(const char *name) { const char *spos = name; while (*name) { - if (isspace(*name) && isalnum(name[1])) + if (isspace(*name) && safe_ascii_char(name[1])) spos = name + 1; name++; } @@ -683,12 +693,12 @@ static void copy_valid_id_string(struct snd_card *card, const char *src, { char *id = card->id; - while (*nid && !isalnum(*nid)) + while (*nid && !safe_ascii_char(*nid)) nid++; if (isdigit(*nid)) *id++ = isalpha(*src) ? *src : 'D'; while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { - if (isalnum(*nid)) + if (safe_ascii_char(*nid)) *id++ = *nid; nid++; } @@ -783,7 +793,7 @@ static ssize_t id_store(struct device *dev, struct device_attribute *attr, for (idx = 0; idx < copy; idx++) { c = buf[idx]; - if (!isalnum(c) && c != '_' && c != '-') + if (!safe_ascii_char(c) && c != '_' && c != '-') return -EINVAL; } memcpy(buf1, buf, copy); @@ -964,7 +974,7 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer) #endif -#ifdef MODULE +#ifdef CONFIG_MODULES static void snd_card_module_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -992,7 +1002,7 @@ int __init snd_card_info_init(void) if (snd_info_register(entry) < 0) return -ENOMEM; /* freed in error path */ -#ifdef MODULE +#ifdef CONFIG_MODULES entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); if (!entry) return -ENOMEM; diff --git a/sound/core/jack.c b/sound/core/jack.c index e08b2c4fbd1a..e4bcecdf89b7 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -37,11 +37,15 @@ static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = { }; #endif /* CONFIG_SND_JACK_INPUT_DEV */ +static void snd_jack_remove_debugfs(struct snd_jack *jack); + static int snd_jack_dev_disconnect(struct snd_device *device) { -#ifdef CONFIG_SND_JACK_INPUT_DEV struct snd_jack *jack = device->device_data; + snd_jack_remove_debugfs(jack); + +#ifdef CONFIG_SND_JACK_INPUT_DEV guard(mutex)(&jack->input_dev_lock); if (!jack->input_dev) return 0; @@ -381,10 +385,14 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack, return 0; } -static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl) +static void snd_jack_remove_debugfs(struct snd_jack *jack) { - debugfs_remove(jack_kctl->jack_debugfs_root); - jack_kctl->jack_debugfs_root = NULL; + struct snd_jack_kctl *jack_kctl; + + list_for_each_entry(jack_kctl, &jack->kctl_list, list) { + debugfs_remove(jack_kctl->jack_debugfs_root); + jack_kctl->jack_debugfs_root = NULL; + } } #else /* CONFIG_SND_JACK_INJECTION_DEBUG */ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack, @@ -393,7 +401,7 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack, return 0; } -static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl) +static void snd_jack_remove_debugfs(struct snd_jack *jack) { } #endif /* CONFIG_SND_JACK_INJECTION_DEBUG */ @@ -404,7 +412,6 @@ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) jack_kctl = kctl->private_data; if (jack_kctl) { - snd_jack_debugfs_clear_inject_node(jack_kctl); list_del(&jack_kctl->list); kfree(jack_kctl); } @@ -497,8 +504,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, .dev_free = snd_jack_dev_free, #ifdef CONFIG_SND_JACK_INPUT_DEV .dev_register = snd_jack_dev_register, - .dev_disconnect = snd_jack_dev_disconnect, #endif /* CONFIG_SND_JACK_INPUT_DEV */ + .dev_disconnect = snd_jack_dev_disconnect, }; if (initial_kctl) { diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index f901504b5afc..b3853583d2ae 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -17,7 +17,17 @@ #include <asm/set_memory.h> #endif #include <sound/memalloc.h> -#include "memalloc_local.h" + +struct snd_malloc_ops { + void *(*alloc)(struct snd_dma_buffer *dmab, size_t size); + void (*free)(struct snd_dma_buffer *dmab); + dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset); + struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset); + unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab, + unsigned int ofs, unsigned int size); + int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area); + void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode); +}; #define DEFAULT_GFP \ (GFP_KERNEL | \ @@ -26,10 +36,6 @@ static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab); -#ifdef CONFIG_SND_DMA_SGBUF -static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size); -#endif - static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) { const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab); @@ -490,15 +496,26 @@ static const struct snd_malloc_ops snd_dma_dev_ops = { /* * Write-combined pages */ -/* x86-specific allocations */ #ifdef CONFIG_SND_DMA_SGBUF +/* x86-specific allocations */ static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size) { - return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true); + void *p = do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true); + + if (!p) + return NULL; + dmab->addr = dma_map_single(dmab->dev.dev, p, size, DMA_BIDIRECTIONAL); + if (dma_mapping_error(dmab->dev.dev, dmab->addr)) { + do_free_pages(dmab->area, size, true); + return NULL; + } + return p; } static void snd_dma_wc_free(struct snd_dma_buffer *dmab) { + dma_unmap_single(dmab->dev.dev, dmab->addr, dmab->bytes, + DMA_BIDIRECTIONAL); do_free_pages(dmab->area, dmab->bytes, true); } @@ -506,7 +523,8 @@ static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab, struct vm_area_struct *area) { area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return snd_dma_continuous_mmap(dmab, area); + return dma_mmap_coherent(dmab->dev.dev, area, + dmab->area, dmab->addr, dmab->bytes); } #else static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size) @@ -525,7 +543,7 @@ static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab, return dma_mmap_wc(dmab->dev.dev, area, dmab->area, dmab->addr, dmab->bytes); } -#endif /* CONFIG_SND_DMA_SGBUF */ +#endif static const struct snd_malloc_ops snd_dma_wc_ops = { .alloc = snd_dma_wc_alloc, @@ -541,16 +559,8 @@ static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size) struct sg_table *sgt; void *p; -#ifdef CONFIG_SND_DMA_SGBUF - if (cpu_feature_enabled(X86_FEATURE_XENPV)) - return snd_dma_sg_fallback_alloc(dmab, size); -#endif sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir, DEFAULT_GFP, 0); -#ifdef CONFIG_SND_DMA_SGBUF - if (!sgt && !get_dma_ops(dmab->dev.dev)) - return snd_dma_sg_fallback_alloc(dmab, size); -#endif if (!sgt) return NULL; @@ -667,125 +677,64 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = { .get_chunk_size = snd_dma_noncontig_get_chunk_size, }; -/* x86-specific SG-buffer with WC pages */ #ifdef CONFIG_SND_DMA_SGBUF -#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it))) - -static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size) -{ - void *p = snd_dma_noncontig_alloc(dmab, size); - struct sg_table *sgt = dmab->private_data; - struct sg_page_iter iter; - - if (!p) - return NULL; - if (dmab->dev.type != SNDRV_DMA_TYPE_DEV_WC_SG) - return p; - for_each_sgtable_page(sgt, &iter, 0) - set_memory_wc(sg_wc_address(&iter), 1); - return p; -} - -static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab) -{ - struct sg_table *sgt = dmab->private_data; - struct sg_page_iter iter; - - for_each_sgtable_page(sgt, &iter, 0) - set_memory_wb(sg_wc_address(&iter), 1); - snd_dma_noncontig_free(dmab); -} - -static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab, - struct vm_area_struct *area) -{ - area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return dma_mmap_noncontiguous(dmab->dev.dev, area, - dmab->bytes, dmab->private_data); -} - -static const struct snd_malloc_ops snd_dma_sg_wc_ops = { - .alloc = snd_dma_sg_wc_alloc, - .free = snd_dma_sg_wc_free, - .mmap = snd_dma_sg_wc_mmap, - .sync = snd_dma_noncontig_sync, - .get_addr = snd_dma_noncontig_get_addr, - .get_page = snd_dma_noncontig_get_page, - .get_chunk_size = snd_dma_noncontig_get_chunk_size, -}; - /* Fallback SG-buffer allocations for x86 */ struct snd_dma_sg_fallback { - bool use_dma_alloc_coherent; + struct sg_table sgt; /* used by get_addr - must be the first item */ size_t count; struct page **pages; - /* DMA address array; the first page contains #pages in ~PAGE_MASK */ - dma_addr_t *addrs; + unsigned int *npages; }; static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab, struct snd_dma_sg_fallback *sgbuf) { + bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG; size_t i, size; - if (sgbuf->pages && sgbuf->addrs) { + if (sgbuf->pages && sgbuf->npages) { i = 0; while (i < sgbuf->count) { - if (!sgbuf->pages[i] || !sgbuf->addrs[i]) - break; - size = sgbuf->addrs[i] & ~PAGE_MASK; - if (WARN_ON(!size)) + size = sgbuf->npages[i]; + if (!size) break; - if (sgbuf->use_dma_alloc_coherent) - dma_free_coherent(dmab->dev.dev, size << PAGE_SHIFT, - page_address(sgbuf->pages[i]), - sgbuf->addrs[i] & PAGE_MASK); - else - do_free_pages(page_address(sgbuf->pages[i]), - size << PAGE_SHIFT, false); + do_free_pages(page_address(sgbuf->pages[i]), + size << PAGE_SHIFT, wc); i += size; } } kvfree(sgbuf->pages); - kvfree(sgbuf->addrs); + kvfree(sgbuf->npages); kfree(sgbuf); } +/* fallback manual S/G buffer allocations */ static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size) { + bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG; struct snd_dma_sg_fallback *sgbuf; struct page **pagep, *curp; - size_t chunk, npages; - dma_addr_t *addrp; + size_t chunk; dma_addr_t addr; + unsigned int idx, npages; void *p; - /* correct the type */ - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) - dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK; - else if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK; - sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); if (!sgbuf) return NULL; - sgbuf->use_dma_alloc_coherent = cpu_feature_enabled(X86_FEATURE_XENPV); size = PAGE_ALIGN(size); sgbuf->count = size >> PAGE_SHIFT; sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL); - sgbuf->addrs = kvcalloc(sgbuf->count, sizeof(*sgbuf->addrs), GFP_KERNEL); - if (!sgbuf->pages || !sgbuf->addrs) + sgbuf->npages = kvcalloc(sgbuf->count, sizeof(*sgbuf->npages), GFP_KERNEL); + if (!sgbuf->pages || !sgbuf->npages) goto error; pagep = sgbuf->pages; - addrp = sgbuf->addrs; - chunk = (PAGE_SIZE - 1) << PAGE_SHIFT; /* to fit in low bits in addrs */ + chunk = size; + idx = 0; while (size > 0) { chunk = min(size, chunk); - if (sgbuf->use_dma_alloc_coherent) - p = dma_alloc_coherent(dmab->dev.dev, chunk, &addr, DEFAULT_GFP); - else - p = do_alloc_pages(dmab->dev.dev, chunk, &addr, false); + p = do_alloc_pages(dmab->dev.dev, chunk, &addr, wc); if (!p) { if (chunk <= PAGE_SIZE) goto error; @@ -797,27 +746,33 @@ static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size) size -= chunk; /* fill pages */ npages = chunk >> PAGE_SHIFT; - *addrp = npages; /* store in lower bits */ + sgbuf->npages[idx] = npages; + idx += npages; curp = virt_to_page(p); - while (npages--) { + while (npages--) *pagep++ = curp++; - *addrp++ |= addr; - addr += PAGE_SIZE; - } } - p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL); - if (!p) + if (sg_alloc_table_from_pages(&sgbuf->sgt, sgbuf->pages, sgbuf->count, + 0, sgbuf->count << PAGE_SHIFT, GFP_KERNEL)) goto error; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK) - set_pages_array_wc(sgbuf->pages, sgbuf->count); + if (dma_map_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0)) + goto error_dma_map; + + p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL); + if (!p) + goto error_vmap; dmab->private_data = sgbuf; /* store the first page address for convenience */ - dmab->addr = sgbuf->addrs[0] & PAGE_MASK; + dmab->addr = snd_sgbuf_get_addr(dmab, 0); return p; + error_vmap: + dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0); + error_dma_map: + sg_free_table(&sgbuf->sgt); error: __snd_dma_sg_fallback_free(dmab, sgbuf); return NULL; @@ -827,36 +782,46 @@ static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab) { struct snd_dma_sg_fallback *sgbuf = dmab->private_data; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK) - set_pages_array_wb(sgbuf->pages, sgbuf->count); vunmap(dmab->area); + dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0); + sg_free_table(&sgbuf->sgt); __snd_dma_sg_fallback_free(dmab, dmab->private_data); } -static dma_addr_t snd_dma_sg_fallback_get_addr(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_dma_sg_fallback *sgbuf = dmab->private_data; - size_t index = offset >> PAGE_SHIFT; - - return (sgbuf->addrs[index] & PAGE_MASK) | (offset & ~PAGE_MASK); -} - static int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab, struct vm_area_struct *area) { struct snd_dma_sg_fallback *sgbuf = dmab->private_data; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK) + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); return vm_map_pages(area, sgbuf->pages, sgbuf->count); } -static const struct snd_malloc_ops snd_dma_sg_fallback_ops = { - .alloc = snd_dma_sg_fallback_alloc, +static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + int type = dmab->dev.type; + void *p; + + /* try the standard DMA API allocation at first */ + if (type == SNDRV_DMA_TYPE_DEV_WC_SG) + dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC; + else + dmab->dev.type = SNDRV_DMA_TYPE_DEV; + p = __snd_dma_alloc_pages(dmab, size); + if (p) + return p; + + dmab->dev.type = type; /* restore the type */ + return snd_dma_sg_fallback_alloc(dmab, size); +} + +static const struct snd_malloc_ops snd_dma_sg_ops = { + .alloc = snd_dma_sg_alloc, .free = snd_dma_sg_fallback_free, .mmap = snd_dma_sg_fallback_mmap, - .get_addr = snd_dma_sg_fallback_get_addr, + /* reuse noncontig helper */ + .get_addr = snd_dma_noncontig_get_addr, /* reuse vmalloc helpers */ .get_page = snd_dma_vmalloc_get_page, .get_chunk_size = snd_dma_vmalloc_get_chunk_size, @@ -927,15 +892,12 @@ static const struct snd_malloc_ops *snd_dma_ops[] = { [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops, [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops, #ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops, + [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, #endif #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ -#ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_SG_FALLBACK] = &snd_dma_sg_fallback_ops, - [SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK] = &snd_dma_sg_fallback_ops, -#endif #endif /* CONFIG_HAS_DMA */ }; diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h deleted file mode 100644 index 8b19f3a68a4b..000000000000 --- a/sound/core/memalloc_local.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#ifndef __MEMALLOC_LOCAL_H -#define __MEMALLOC_LOCAL_H - -struct snd_malloc_ops { - void *(*alloc)(struct snd_dma_buffer *dmab, size_t size); - void (*free)(struct snd_dma_buffer *dmab); - dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset); - struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset); - unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab, - unsigned int ofs, unsigned int size); - int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area); - void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode); -}; - -#endif /* __MEMALLOC_LOCAL_H */ diff --git a/sound/core/memory.c b/sound/core/memory.c index 2d2d0094c897..d683442b4c97 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -27,38 +27,43 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size if (import_ubuf(ITER_DEST, dst, count, &iter)) return -EFAULT; - return copy_to_iter_fromio(&iter, (const void __iomem *)src, count); + if (copy_to_iter_fromio((const void __iomem *)src, count, &iter) != count) + return -EFAULT; + return 0; } EXPORT_SYMBOL(copy_to_user_fromio); /** * copy_to_iter_fromio - copy data from mmio-space to iov_iter - * @dst: the destination iov_iter * @src: the source pointer on mmio * @count: the data size to copy in bytes + * @dst: the destination iov_iter * * Copies the data from mmio-space to iov_iter. * - * Return: Zero if successful, or non-zero on failure. + * Return: number of bytes to be copied */ -int copy_to_iter_fromio(struct iov_iter *dst, const void __iomem *src, - size_t count) +size_t copy_to_iter_fromio(const void __iomem *src, size_t count, + struct iov_iter *dst) { #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_to_iter((const void __force *)src, count, dst) == count ? 0 : -EFAULT; + return copy_to_iter((const void __force *)src, count, dst); #else char buf[256]; + size_t res = 0; + while (count) { size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); memcpy_fromio(buf, (void __iomem *)src, c); if (copy_to_iter(buf, c, dst) != c) - return -EFAULT; + return res; count -= c; src += c; + res += c; } - return 0; + return res; #endif } EXPORT_SYMBOL(copy_to_iter_fromio); @@ -79,37 +84,43 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter)) return -EFAULT; - return copy_from_iter_toio((void __iomem *)dst, &iter, count); + if (copy_from_iter_toio((void __iomem *)dst, count, &iter) != count) + return -EFAULT; + return 0; } EXPORT_SYMBOL(copy_from_user_toio); /** * copy_from_iter_toio - copy data from iov_iter to mmio-space * @dst: the destination pointer on mmio-space - * @src: the source iov_iter * @count: the data size to copy in bytes + * @src: the source iov_iter * * Copies the data from iov_iter to mmio-space. * - * Return: Zero if successful, or non-zero on failure. + * Return: number of bytes to be copied */ -int copy_from_iter_toio(void __iomem *dst, struct iov_iter *src, size_t count) +size_t copy_from_iter_toio(void __iomem *dst, size_t count, + struct iov_iter *src) { #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_from_iter((void __force *)dst, count, src) == count ? 0 : -EFAULT; + return copy_from_iter((void __force *)dst, count, src); #else char buf[256]; + size_t res = 0; + while (count) { size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); if (copy_from_iter(buf, c, src) != c) - return -EFAULT; + return res; memcpy_toio(dst, buf, c); count -= c; dst += c; + res += c; } - return 0; + return res; #endif } EXPORT_SYMBOL(copy_from_iter_toio); diff --git a/sound/core/misc.c b/sound/core/misc.c index d32a19976a2b..c2fda3bd90a0 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -13,20 +13,6 @@ #include <linux/fs.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) { @@ -36,63 +22,6 @@ void release_and_free_resource(struct resource *res) } EXPORT_SYMBOL(release_and_free_resource); -#ifdef CONFIG_SND_VERBOSE_PRINTK -/* strip the leading path if the given path is absolute */ -static const char *sanity_file_name(const char *path) -{ - if (*path == '/') - return strrchr(path, '/') + 1; - else - return path; -} -#endif - -#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; -#ifdef CONFIG_SND_VERBOSE_PRINTK - int kern_level; - struct va_format vaf; - char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; - bool level_found = false; -#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; - - while ((kern_level = printk_get_level(vaf.fmt)) != 0) { - const char *end_of_header = printk_skip_level(vaf.fmt); - - /* Ignore KERN_CONT. We print filename:line for each piece. */ - if (kern_level >= '0' && kern_level <= '7') { - memcpy(verbose_fmt, vaf.fmt, end_of_header - vaf.fmt); - level_found = true; - } - - vaf.fmt = end_of_header; - } - - if (!level_found && 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> /** diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile index ae25edcc3b42..d5f48ae6ba96 100644 --- a/sound/core/oss/Makefile +++ b/sound/core/oss/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -snd-mixer-oss-objs := mixer_oss.o +snd-mixer-oss-y := mixer_oss.o snd-pcm-oss-y := pcm_oss.o snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \ diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 6a0508093ea6..05fc8911479c 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -412,7 +412,6 @@ 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, }; @@ -510,7 +509,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(id.name, name, sizeof(id.name)); id.index = index; - return snd_ctl_find_id_locked(card, &id); + return snd_ctl_find_id(card, &id); } static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, @@ -526,7 +525,7 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_numid_locked(card, numid); + kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -559,7 +558,7 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_numid_locked(card, numid); + kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -619,7 +618,7 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_numid_locked(card, numid); + kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -656,7 +655,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; guard(rwsem_read)(&card->controls_rwsem); - kctl = snd_ctl_find_numid_locked(card, numid); + kctl = snd_ctl_find_numid(card, numid); if (!kctl) return; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -901,8 +900,8 @@ static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn) struct slot *p = chn->private_data; if (p) { if (p->allocated && p->assigned) { - kfree_const(p->assigned->name); - kfree_const(p->assigned); + kfree(p->assigned->name); + kfree(p->assigned); } kfree(p); } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 7386982cf40e..4683b9139c56 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -3106,7 +3106,6 @@ static const struct file_operations snd_pcm_oss_f_reg = .write = snd_pcm_oss_write, .open = snd_pcm_oss_open, .release = snd_pcm_oss_release, - .llseek = no_llseek, .poll = snd_pcm_oss_poll, .unlocked_ioctl = snd_pcm_oss_ioctl, .compat_ioctl = snd_pcm_oss_ioctl_compat, diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index 50a6b50f5db4..7b76cf64157e 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -74,7 +74,6 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *handle, size_t extra, struct snd_pcm_plugin **ret); int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin); -int snd_pcm_plugin_clear(struct snd_pcm_plugin **first); int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames); snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size); snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size); @@ -139,8 +138,6 @@ int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, size_t dst_offset, 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); #else static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; } @@ -160,7 +157,7 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames); #ifdef PLUGIN_DEBUG -#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args) +#define pdprintf(fmt, args...) pr_debug("plugin: " fmt, ##args) #else #define pdprintf(fmt, args...) #endif diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 98269119347f..b56eeda5e30e 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -294,7 +294,7 @@ static int rate_action(struct snd_pcm_plugin *plugin, default: break; } - return 0; /* silenty ignore other actions */ + return 0; /* silently ignore other actions */ } int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug, diff --git a/sound/core/pcm.c b/sound/core/pcm.c index dc37f3508dc7..290690fc2abc 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -462,6 +462,9 @@ 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); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + snd_iprintf(buffer, "xrun_counter: %d\n", substream->xrun_counter); +#endif } #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -970,6 +973,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, substream->pid = get_pid(task_pid(current)); pstr->substream_opened++; *rsubstream = substream; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + substream->xrun_counter = 0; +#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ return 0; } diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 494ec0c207fa..b134a51b3fd5 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -349,6 +349,37 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); +int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status != DMA_PAUSED) + dmaengine_synchronize(prtd->dma_chan); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop); + +static void __snd_dmaengine_pcm_close(struct snd_pcm_substream *substream, + bool release_channel) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status == DMA_PAUSED) + dmaengine_terminate_async(prtd->dma_chan); + + dmaengine_synchronize(prtd->dma_chan); + if (release_channel) + dma_release_channel(prtd->dma_chan); + kfree(prtd); +} + /** * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream * @substream: PCM substream @@ -357,11 +388,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); */ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) { - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - dmaengine_synchronize(prtd->dma_chan); - kfree(prtd); - + __snd_dmaengine_pcm_close(substream, false); return 0; } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); @@ -377,12 +404,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); */ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) { - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - dmaengine_synchronize(prtd->dma_chan); - dma_release_channel(prtd->dma_chan); - kfree(prtd); - + __snd_dmaengine_pcm_close(substream, true); return 0; } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); @@ -470,4 +492,5 @@ int snd_dmaengine_pcm_refine_runtime_hwparams( } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams); +MODULE_DESCRIPTION("PCM dmaengine helper APIs"); MODULE_LICENSE("GPL"); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6f73b3c2c205..6eaa950504cf 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -184,6 +184,9 @@ void __snd_pcm_xrun(struct snd_pcm_substream *substream) pcm_warn(substream->pcm, "XRUN: %s\n", name); dump_stack_on_xrun(substream); } +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + substream->xrun_counter++; +#endif } #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -516,21 +519,38 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, EXPORT_SYMBOL(snd_pcm_set_ops); /** - * snd_pcm_set_sync - set the PCM sync id + * snd_pcm_set_sync_per_card - set the PCM sync id with card number * @substream: the pcm substream + * @params: modified hardware parameters + * @id: identifier (max 12 bytes) + * @len: identifier length (max 12 bytes) + * + * Sets the PCM sync identifier for the card with zero padding. * - * Sets the PCM sync identifier for the card. + * User space or any user should use this 16-byte identifier for a comparison only + * to check if two IDs are similar or different. Special case is the identifier + * containing only zeros. Interpretation for this combination is - empty (not set). + * The contents of the identifier should not be interpreted in any other way. + * + * The synchronization ID must be unique per clock source (usually one sound card, + * but multiple soundcard may use one PCM word clock source which means that they + * are fully synchronized). + * + * This routine composes this ID using card number in first four bytes and + * 12-byte additional ID. When other ID composition is used (e.g. for multiple + * sound cards), make sure that the composition does not clash with this + * composition scheme. */ -void snd_pcm_set_sync(struct snd_pcm_substream *substream) +void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + const unsigned char *id, unsigned int len) { - struct snd_pcm_runtime *runtime = substream->runtime; - - runtime->sync.id32[0] = substream->pcm->card->number; - runtime->sync.id32[1] = -1; - runtime->sync.id32[2] = -1; - runtime->sync.id32[3] = -1; + *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number); + len = min(12, len); + memcpy(params->sync + 4, id, len); + memset(params->sync + 4 + len, 0, 12 - len); } -EXPORT_SYMBOL(snd_pcm_set_sync); +EXPORT_SYMBOL_GPL(snd_pcm_set_sync_per_card); /* * Standard ioctl routine @@ -1810,6 +1830,18 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, return 0; } +static int snd_pcm_lib_ioctl_sync_id(struct snd_pcm_substream *substream, + void *arg) +{ + static const unsigned char id[12] = { 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff }; + + if (substream->runtime->std_sync_id) + snd_pcm_set_sync_per_card(substream, arg, id, sizeof(id)); + return 0; +} + /** * snd_pcm_lib_ioctl - a generic PCM ioctl callback * @substream: the pcm substream instance @@ -1831,6 +1863,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, return snd_pcm_lib_ioctl_channel_info(substream, arg); case SNDRV_PCM_IOCTL1_FIFO_SIZE: return snd_pcm_lib_ioctl_fifo_size(substream, arg); + case SNDRV_PCM_IOCTL1_SYNC_ID: + return snd_pcm_lib_ioctl_sync_id(substream, arg); } return -ENXIO; } @@ -2556,6 +2590,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, struct snd_kcontrol_new knew = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, .info = pcm_chmap_ctl_info, diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 506386959f08..ea3941f8666b 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -9,7 +9,6 @@ #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> @@ -184,7 +183,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, struct snd_pcm_substream *substream = entry->private_data; struct snd_card *card = substream->pcm->card; char line[64], str[64]; - size_t size; + unsigned long size; struct snd_dma_buffer new_dmab; guard(mutex)(&substream->pcm->open_mutex); @@ -194,7 +193,10 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, } if (!snd_info_get_line(buffer, line, sizeof(line))) { snd_info_get_str(str, line, sizeof(str)); - size = simple_strtoul(str, NULL, 10) * 1024; + buffer->error = kstrtoul(str, 10, &size); + if (buffer->error != 0) + return; + size *= 1024; if ((size != 0 && size < 8192) || size > substream->dma_max) { buffer->error = -EINVAL; return; @@ -210,7 +212,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, substream->stream, size, &new_dmab) < 0) { buffer->error = -ENOMEM; - pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", + pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %lu\n", substream->pcm->card->number, substream->pcm->device, substream->stream ? 'c' : 'p', substream->number, substream->pcm->name, size); @@ -497,61 +499,3 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) return 0; } 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); - 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 5588b6a1ee8b..4f556211bb56 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -494,18 +494,20 @@ EXPORT_SYMBOL(snd_pcm_format_set_silence); int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw) { int i; + unsigned int rmin, rmax; + + rmin = UINT_MAX; + rmax = 0; for (i = 0; i < (int)snd_pcm_known_rates.count; i++) { if (hw->rates & (1 << i)) { - hw->rate_min = snd_pcm_known_rates.list[i]; - break; - } - } - for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) { - if (hw->rates & (1 << i)) { - hw->rate_max = snd_pcm_known_rates.list[i]; - break; + rmin = min(rmin, snd_pcm_known_rates.list[i]); + rmax = max(rmax, snd_pcm_known_rates.list[i]); } } + if (rmin > rmax) + return -EINVAL; + hw->rate_min = rmin; + hw->rate_max = rmax; return 0; } EXPORT_SYMBOL(snd_pcm_hw_limit_rates); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0b76e76823d2..6c2b6a62d9d2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -533,6 +533,12 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, SNDRV_PCM_INFO_MMAP_VALID); } + err = snd_pcm_ops_ioctl(substream, + SNDRV_PCM_IOCTL1_SYNC_ID, + params); + if (err < 0) + return err; + return 0; } @@ -576,7 +582,7 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(no_free_ptr(params)); + return PTR_ERR(params); err = snd_pcm_hw_refine(substream, params); if (err < 0) @@ -866,7 +872,7 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(no_free_ptr(params)); + return PTR_ERR(params); err = snd_pcm_hw_params(substream, params); if (err < 0) @@ -1775,6 +1781,8 @@ static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, snd_pcm_state_t state) { struct snd_pcm_runtime *runtime = substream->runtime; + if (runtime->state != SNDRV_PCM_STATE_SUSPENDED) + return -EBADFD; if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) return -ENOSYS; runtime->trigger_master = substream; @@ -2242,12 +2250,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) bool nonatomic = substream->pcm->nonatomic; CLASS(fd, f)(fd); - if (!f.file) + if (fd_empty(f)) return -EBADFD; - if (!is_pcm_file(f.file)) + if (!is_pcm_file(fd_file(f))) return -EBADFD; - pcm_file = f.file->private_data; + pcm_file = fd_file(f)->private_data; substream1 = pcm_file->substream; if (substream == substream1) @@ -2410,13 +2418,17 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, return snd_interval_refine(hw_param_interval(params, rule->var), &t); } -#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ||\ + SNDRV_PCM_RATE_128000 != 1 << 19 #error "Change this table" #endif +/* NOTE: the list is unsorted! */ static const unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000 + 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000, + /* extended */ + 12000, 24000, 128000 }; const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { @@ -3103,7 +3115,7 @@ struct snd_pcm_sync_ptr32 { } c; } __packed; -/* recalcuate the boundary within 32bit */ +/* recalculate the boundary within 32bit */ static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t boundary; @@ -3233,9 +3245,9 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels); + bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); if (IS_ERR(bufs)) - return PTR_ERR(no_free_ptr(bufs)); + return PTR_ERR(bufs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) result = snd_pcm_lib_writev(substream, bufs, xfern.frames); else @@ -3762,6 +3774,26 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file #endif /* coherent mmap */ /* + * snd_pcm_mmap_data_open - increase the mmap counter + */ +static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ + struct snd_pcm_substream *substream = area->vm_private_data; + + atomic_inc(&substream->mmap_count); +} + +/* + * snd_pcm_mmap_data_close - decrease the mmap counter + */ +static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + struct snd_pcm_substream *substream = area->vm_private_data; + + atomic_dec(&substream->mmap_count); +} + +/* * fault callback for mmapping a RAM page */ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) @@ -3781,9 +3813,11 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) return VM_FAULT_SIGBUS; if (substream->ops->page) page = substream->ops->page(substream, offset); - else if (!snd_pcm_get_dma_buf(substream)) + else if (!snd_pcm_get_dma_buf(substream)) { + if (WARN_ON_ONCE(!runtime->dma_area)) + return VM_FAULT_SIGBUS; page = virt_to_page(runtime->dma_area + offset); - else + } else page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset); if (!page) return VM_FAULT_SIGBUS; @@ -4024,7 +4058,7 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, oparams = memdup_user(_oparams, sizeof(*oparams)); if (IS_ERR(oparams)) - return PTR_ERR(no_free_ptr(oparams)); + return PTR_ERR(oparams); snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_refine(substream, params); if (err < 0) @@ -4053,7 +4087,7 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, oparams = memdup_user(_oparams, sizeof(*oparams)); if (IS_ERR(oparams)) - return PTR_ERR(no_free_ptr(oparams)); + return PTR_ERR(oparams); snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_params(substream, params); @@ -4103,7 +4137,6 @@ const struct file_operations snd_pcm_f_ops[2] = { .write_iter = snd_pcm_writev, .open = snd_pcm_playback_open, .release = snd_pcm_release, - .llseek = no_llseek, .poll = snd_pcm_poll, .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, @@ -4117,7 +4150,6 @@ const struct file_operations snd_pcm_f_ops[2] = { .read_iter = snd_pcm_readv, .open = snd_pcm_capture_open, .release = snd_pcm_release, - .llseek = no_llseek, .poll = snd_pcm_poll, .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index c43484b22b34..ab0e5bd70f8f 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -108,8 +108,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream) if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) return; sprintf(timer->name, "PCM %s %i-%i-%i", - substream->stream == SNDRV_PCM_STREAM_CAPTURE ? - "capture" : "playback", + snd_pcm_direction_name(substream->stream), tid.card, tid.device, tid.subdevice); timer->hw = snd_pcm_timer; if (snd_device_register(timer->card, timer) < 0) { diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h index 350b40b906ca..adb9b1f3bbfa 100644 --- a/sound/core/pcm_trace.h +++ b/sound/core/pcm_trace.h @@ -95,7 +95,7 @@ TRACE_EVENT(hw_ptr_error, __entry->device = (substream)->pcm->device; __entry->number = (substream)->number; __entry->stream = (substream)->stream; - __assign_str(reason, why); + __assign_str(reason); ), TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s", __entry->card, __entry->device, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 7accf9a1ddf4..70a958ac1112 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -629,12 +629,15 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->subdevice = substream->number; info->stream = substream->stream; info->flags = rmidi->info_flags; + if (substream->inactive) + info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE; strcpy(info->id, rmidi->id); strcpy(info->name, rmidi->name); strcpy(info->subname, substream->name); info->subdevices_count = substream->pstr->substream_count; info->subdevices_avail = (substream->pstr->substream_count - substream->pstr->substream_opened); + info->tied_device = rmidi->tied_device; return 0; } @@ -724,8 +727,9 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); if (!newbuf) return -ENOMEM; - guard(spinlock_irq)(&substream->lock); + spin_lock_irq(&substream->lock); if (runtime->buffer_ref) { + spin_unlock_irq(&substream->lock); kvfree(newbuf); return -EBUSY; } @@ -733,6 +737,7 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; __reset_runtime_ptrs(runtime, is_input); + spin_unlock_irq(&substream->lock); kvfree(oldbuf); } runtime->avail_min = params->avail_min; @@ -1784,7 +1789,6 @@ static const struct file_operations snd_rawmidi_f_ops = { .write = snd_rawmidi_write, .open = snd_rawmidi_open, .release = snd_rawmidi_release, - .llseek = no_llseek, .poll = snd_rawmidi_poll, .unlocked_ioctl = snd_rawmidi_ioctl, .compat_ioctl = snd_rawmidi_ioctl_compat, diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index 0374bbf51cd4..e4f58cb985d4 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -62,7 +62,7 @@ config SND_SEQ_VIRMIDI config SND_SEQ_UMP bool "Support for UMP events" - default y if SND_SEQ_UMP_CLIENT + default SND_UMP help Say Y here to enable the support for handling UMP (Universal MIDI Packet) events via ALSA sequencer infrastructure, which is an @@ -71,6 +71,6 @@ config SND_SEQ_UMP among legacy and UMP clients. config SND_SEQ_UMP_CLIENT - def_tristate SND_UMP + def_tristate SND_UMP && SND_SEQ_UMP endif # SND_SEQUENCER diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 990eec7c83ad..0904aa48d88b 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -4,17 +4,17 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ +snd-seq-y := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ seq_fifo.o seq_prioq.o seq_timer.o \ seq_system.o seq_ports.o snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.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-dummy-objs := seq_dummy.o -snd-seq-virmidi-objs := seq_virmidi.o -snd-seq-ump-client-objs := seq_ump_client.o +snd-seq-midi-y := seq_midi.o +snd-seq-midi-emul-y := seq_midi_emul.o +snd-seq-midi-event-y := seq_midi_event.o +snd-seq-dummy-y := seq_dummy.o +snd-seq-virmidi-y := seq_virmidi.o +snd-seq-ump-client-y := seq_ump_client.o obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/ diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile index f1a60878549a..4e4741834208 100644 --- a/sound/core/seq/oss/Makefile +++ b/sound/core/seq/oss/Makefile @@ -4,7 +4,7 @@ # 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 \ +snd-seq-oss-y := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index f0e964b19af7..6163a00bc8de 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -55,7 +55,6 @@ struct seq_oss_chinfo { struct seq_oss_synthinfo { struct snd_seq_oss_arg arg; struct seq_oss_chinfo *ch; - struct seq_oss_synth_sysex *sysex; int nr_voices; int opened; int is_midi; @@ -116,10 +115,6 @@ __poll_t snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_ta void snd_seq_oss_reset(struct seq_oss_devinfo *dp); -/* */ -void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time); - - /* proc interface */ void snd_seq_oss_system_info_read(struct snd_info_buffer *buf); void snd_seq_oss_midi_info_read(struct snd_info_buffer *buf); @@ -161,8 +156,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, ev->dest.port = dest_port; } - -/* misc. functions for proc interface */ -char *enabled_str(bool b); - #endif /* __SEQ_OSS_DEVICE_H */ diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 676246fa02f1..e6d7d83ed0e7 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -111,7 +111,7 @@ snd_seq_oss_create_client(void) /* - * receive annoucement from system port, and check the midi device + * receive announcement from system port, and check the midi device */ static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop) @@ -449,12 +449,6 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp) /* * misc. functions for proc interface */ -char * -enabled_str(bool b) -{ - return b ? "enabled" : "disabled"; -} - static const char * filemode_str(int val) { diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index e3394919daa0..9de47e098b29 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -26,13 +26,6 @@ * definition of synth info records */ -/* sysex buffer */ -struct seq_oss_synth_sysex { - int len; - int skip; - unsigned char buf[MAX_SYSEX_BUFLEN]; -}; - /* synth info */ struct seq_oss_synth { int seq_device; @@ -318,8 +311,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) } snd_use_lock_free(&rec->use_lock); } - kfree(info->sysex); - info->sysex = NULL; kfree(info->ch); info->ch = NULL; } @@ -395,8 +386,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) info = get_synthinfo_nospec(dp, dev); if (!info || !info->opened) return; - if (info->sysex) - info->sysex->len = 0; /* reset sysex */ reset_channels(info); if (info->is_midi) { if (midi_synth_dev.opened <= 0) @@ -408,8 +397,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) dp->file_mode) < 0) { midi_synth_dev.opened--; info->opened = 0; - kfree(info->sysex); - info->sysex = NULL; kfree(info->ch); info->ch = NULL; } @@ -482,63 +469,26 @@ snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev) /* * receive OSS 6 byte sysex packet: - * the full sysex message will be sent if it reaches to the end of data - * (0xff). + * the event is filled and prepared for sending immediately + * (i.e. sysex messages are fragmented) */ int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev) { - int i, send; - unsigned char *dest; - struct seq_oss_synth_sysex *sysex; - struct seq_oss_synthinfo *info; + unsigned char *p; + int len = 6; - info = snd_seq_oss_synth_info(dp, dev); - if (!info) - return -ENXIO; + p = memchr(buf, 0xff, 6); + if (p) + len = p - buf + 1; - sysex = info->sysex; - if (sysex == NULL) { - sysex = kzalloc(sizeof(*sysex), GFP_KERNEL); - if (sysex == NULL) - return -ENOMEM; - info->sysex = sysex; - } - - send = 0; - dest = sysex->buf + sysex->len; - /* copy 6 byte packet to the buffer */ - for (i = 0; i < 6; i++) { - if (buf[i] == 0xff) { - send = 1; - break; - } - dest[i] = buf[i]; - sysex->len++; - if (sysex->len >= MAX_SYSEX_BUFLEN) { - sysex->len = 0; - sysex->skip = 1; - break; - } - } - - if (sysex->len && send) { - if (sysex->skip) { - sysex->skip = 0; - sysex->len = 0; - return -EINVAL; /* skip */ - } - /* copy the data to event record and send it */ - ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; - if (snd_seq_oss_synth_addr(dp, dev, ev)) - return -EINVAL; - ev->data.ext.len = sysex->len; - ev->data.ext.ptr = sysex->buf; - sysex->len = 0; - return 0; - } - - return -EINVAL; /* skip */ + /* copy the data to event record and send it */ + if (snd_seq_oss_synth_addr(dp, dev, ev)) + return -EINVAL; + ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = len; + ev->data.ext.ptr = buf; + return 0; } /* @@ -658,8 +608,8 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) rec->synth_type, rec->synth_subtype, rec->nr_voices); snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", - enabled_str((long)rec->oper.ioctl), - enabled_str((long)rec->oper.load_patch)); + str_enabled_disabled((long)rec->oper.ioctl), + str_enabled_disabled((long)rec->oper.load_patch)); snd_use_lock_free(&rec->use_lock); } } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 42a705141050..073b56dc2225 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -70,7 +70,7 @@ static int bounce_error_event(struct snd_seq_client *client, int err, int atomic, int hop); static int snd_seq_deliver_single_event(struct snd_seq_client *client, struct snd_seq_event *event, - int filter, int atomic, int hop); + int atomic, int hop); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) static void free_ump_info(struct snd_seq_client *client); @@ -525,10 +525,8 @@ static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags) /* * check if the destination client is available, and return the pointer - * if filter is non-zero, client filter bitmap is tested. */ -static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event, - int filter) +static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event) { struct snd_seq_client *dest; @@ -537,11 +535,12 @@ static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event, return NULL; if (! dest->accept_input) goto __not_avail; + if (snd_seq_ev_is_ump(event)) + return dest; /* ok - no filter checks */ + if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) && ! test_bit(event->type, dest->event_filter)) goto __not_avail; - if (filter && !(dest->filter & filter)) - goto __not_avail; return dest; /* ok - accessible */ __not_avail: @@ -585,7 +584,7 @@ static int bounce_error_event(struct snd_seq_client *client, bounce_ev.data.quote.origin = event->dest; bounce_ev.data.quote.event = event; bounce_ev.data.quote.value = -err; /* use positive value */ - result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1); + result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1); if (result < 0) { client->event_lost++; return result; @@ -652,7 +651,7 @@ int __snd_seq_deliver_single_event(struct snd_seq_client *dest, */ static int snd_seq_deliver_single_event(struct snd_seq_client *client, struct snd_seq_event *event, - int filter, int atomic, int hop) + int atomic, int hop) { struct snd_seq_client *dest = NULL; struct snd_seq_client_port *dest_port = NULL; @@ -661,7 +660,7 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, direct = snd_seq_ev_is_direct(event); - dest = get_event_dest_client(event, filter); + dest = get_event_dest_client(event); if (dest == NULL) goto __skip; dest_port = snd_seq_port_use_ptr(dest, event->dest.port); @@ -741,8 +740,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, /* convert time according to flag with subscription */ update_timestamp_of_queue(event, subs->info.queue, subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL); - err = snd_seq_deliver_single_event(client, event, - 0, atomic, hop); + err = snd_seq_deliver_single_event(client, event, atomic, hop); if (err < 0) { /* save first error that occurs and continue */ if (!result) @@ -815,7 +813,7 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) result = deliver_to_subscribers(client, event, atomic, hop); else - result = snd_seq_deliver_single_event(client, event, 0, atomic, hop); + result = snd_seq_deliver_single_event(client, event, atomic, hop); return result; } @@ -1277,10 +1275,16 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, if (client->type != client_info->type) return -EINVAL; - /* check validity of midi_version field */ - if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3) && - client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0) - return -EINVAL; + if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) { + /* check validity of midi_version field */ + if (client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0) + return -EINVAL; + + /* check if UMP is supported in kernel */ + if (!IS_ENABLED(CONFIG_SND_SEQ_UMP) && + client_info->midi_version > 0) + return -EINVAL; + } /* fill the info fields */ if (client_info->name[0]) @@ -1292,6 +1296,10 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, client->midi_version = client_info->midi_version; memcpy(client->event_filter, client_info->event_filter, 32); client->group_filter = client_info->group_filter; + + /* notify the change */ + snd_seq_system_client_ev_client_change(client->number); + return 0; } @@ -1415,6 +1423,9 @@ static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg) if (port) { snd_seq_set_port_info(port, info); snd_seq_port_unlock(port); + /* notify the change */ + snd_seq_system_client_ev_port_change(info->addr.client, + info->addr.port); } return 0; } @@ -1471,7 +1482,7 @@ int snd_seq_client_notify_subscription(int client, int port, event.data.connect.dest = info->dest; event.data.connect.sender = info->sender; - return snd_seq_system_notify(client, port, &event); /* non-atomic */ + return snd_seq_system_notify(client, port, &event, false); /* non-atomic */ } @@ -1718,6 +1729,8 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client, tempo->ppq = tmr->ppq; tempo->skew_value = tmr->skew; tempo->skew_base = tmr->skew_base; + if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 4)) + tempo->tempo_base = tmr->tempo_base; queuefree(queue); return 0; @@ -1739,6 +1752,8 @@ static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, struct snd_seq_queue_tempo *tempo = arg; int result; + if (client->user_pversion < SNDRV_PROTOCOL_VERSION(1, 0, 4)) + tempo->tempo_base = 0; result = snd_seq_set_queue_tempo(client->number, tempo); return result < 0 ? result : 0; } @@ -2221,6 +2236,16 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, error: mutex_unlock(&cptr->ioctl_mutex); snd_seq_client_unlock(cptr); + if (!err && cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO) { + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + snd_seq_system_ump_notify(client, 0, + SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + false); + else + snd_seq_system_ump_notify(client, type - 1, + SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE, + false); + } return err; } #endif @@ -2629,13 +2654,18 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, list_for_each_entry(p, &client->ports_list_head, list) { if (p->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) continue; - snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]\n", + snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]", p->addr.port, p->name, FLAG_PERM_RD(p->capability), FLAG_PERM_WR(p->capability), FLAG_PERM_EX(p->capability), FLAG_PERM_DUPLEX(p->capability), port_direction_name(p->direction)); +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) + if (snd_seq_client_is_midi2(client) && p->is_midi1) + snd_iprintf(buffer, " [MIDI1]"); +#endif + snd_iprintf(buffer, "\n"); snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: "); snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: "); } @@ -2715,7 +2745,6 @@ static const struct file_operations snd_seq_f_ops = .write = snd_seq_write, .open = snd_seq_open, .release = snd_seq_release, - .llseek = no_llseek, .poll = snd_seq_poll, .unlocked_ioctl = snd_seq_ioctl, .compat_ioctl = snd_seq_ioctl_compat, diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 9308194b2d9a..783fc72c2ef6 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -58,6 +58,12 @@ MODULE_PARM_DESC(ports, "number of ports to be created"); module_param(duplex, bool, 0444); MODULE_PARM_DESC(duplex, "create DUPLEX ports"); +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) +static int ump; +module_param(ump, int, 0444); +MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)"); +#endif + struct snd_seq_dummy_port { int client; int port; @@ -152,7 +158,9 @@ static int __init register_client(void) { struct snd_seq_dummy_port *rec1, *rec2; +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) struct snd_seq_client *client; +#endif int i; if (ports < 1) { @@ -166,12 +174,24 @@ register_client(void) if (my_client < 0) return my_client; - /* don't convert events but just pass-through */ +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) client = snd_seq_kernel_client_get(my_client); if (!client) return -EINVAL; - client->filter = SNDRV_SEQ_FILTER_NO_CONVERT; + switch (ump) { + case 1: + client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0; + break; + case 2: + client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0; + break; + default: + /* don't convert events but just pass-through */ + client->filter = SNDRV_SEQ_FILTER_NO_CONVERT; + break; + } snd_seq_kernel_client_put(client); +#endif /* create ports */ for (i = 0; i < ports; i++) { diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index ca631ca4f2c6..cc2f8e846584 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -362,6 +362,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port, port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; } + port->is_midi1 = !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1); + return 0; } @@ -399,6 +401,9 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port, info->time_queue = port->time_queue; } + if (port->is_midi1) + info->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1; + /* UMP direction and group */ info->direction = port->direction; info->ump_group = port->ump_group; diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h index b111382f697a..b3b35018cb82 100644 --- a/sound/core/seq/seq_ports.h +++ b/sound/core/seq/seq_ports.h @@ -7,6 +7,7 @@ #define __SND_SEQ_PORTS_H #include <sound/seq_kernel.h> +#include <sound/ump_convert.h> #include "seq_lock.h" /* list of 'exported' ports */ @@ -42,17 +43,6 @@ struct snd_seq_port_subs_info { int (*close)(void *private_data, struct snd_seq_port_subscribe *info); }; -/* context for converting from legacy control event to UMP packet */ -struct snd_seq_ump_midi2_bank { - bool rpn_set; - bool nrpn_set; - bool bank_set; - unsigned char cc_rpn_msb, cc_rpn_lsb; - unsigned char cc_nrpn_msb, cc_nrpn_lsb; - unsigned char cc_data_msb, cc_data_lsb; - unsigned char cc_bank_msb, cc_bank_lsb; -}; - struct snd_seq_client_port { struct snd_seq_addr addr; /* client/port number */ @@ -87,8 +77,10 @@ struct snd_seq_client_port { unsigned char direction; unsigned char ump_group; + bool is_midi1; /* keep MIDI 1.0 protocol */ + #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - struct snd_seq_ump_midi2_bank midi2_bank[16]; /* per channel */ + struct ump_cvt_to_ump_bank midi2_bank[16]; /* per channel */ #endif }; diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 500ee6b19c71..5df26788dda4 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -460,7 +460,8 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client, return -EPERM; } - result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq); + result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq, + info->tempo_base); if (result >= 0 && info->skew_base > 0) result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base); @@ -724,7 +725,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, tmr = q->timer; if (tmr->tempo) - bpm = 60000000 / tmr->tempo; + bpm = (60000 * tmr->tempo_base) / tmr->tempo; else bpm = 0; @@ -741,6 +742,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped"); snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq); snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo); + snd_iprintf(buffer, "tempo base : %d ns\n", tmr->tempo_base); snd_iprintf(buffer, "current BPM : %d\n", bpm); snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec); snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick); diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h index c69105dc1a10..74cc31aacdac 100644 --- a/sound/core/seq/seq_queue.h +++ b/sound/core/seq/seq_queue.h @@ -84,7 +84,6 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop); int snd_seq_queue_check_access(int queueid, int client); int snd_seq_queue_timer_set_tempo(int queueid, int client, struct snd_seq_queue_tempo *info); int snd_seq_queue_set_owner(int queueid, int client, int locked); -int snd_seq_queue_set_locked(int queueid, int client, int locked); int snd_seq_queue_timer_open(int queueid); int snd_seq_queue_timer_close(int queueid); int snd_seq_queue_use(int queueid, int client, int use); diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 80267290190d..853920f79016 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -49,12 +49,14 @@ static int sysclient = -1; /* port id numbers for this client */ static int announce_port = -1; +/* number of subscriptions to announce port */ +static int announce_subscribed; /* fill standard header data, source port & channel are filled in */ static int setheader(struct snd_seq_event * ev, int client, int port) { - if (announce_port < 0) + if (announce_port < 0 || !announce_subscribed) return -ENODEV; memset(ev, 0, sizeof(struct snd_seq_event)); @@ -76,26 +78,27 @@ static int setheader(struct snd_seq_event * ev, int client, int port) /* entry points for broadcasting system events */ -void snd_seq_system_broadcast(int client, int port, int type) +void snd_seq_system_broadcast(int client, int port, int type, bool atomic) { struct snd_seq_event ev; if (setheader(&ev, client, port) < 0) return; ev.type = type; - snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); + snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0); } EXPORT_SYMBOL_GPL(snd_seq_system_broadcast); /* entry points for broadcasting system events */ -int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev) +int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev, + bool atomic) { ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; ev->source.client = sysclient; ev->source.port = announce_port; ev->dest.client = client; ev->dest.port = port; - return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); + return snd_seq_kernel_client_dispatch(sysclient, ev, atomic, 0); } /* call-back handler for timer events */ @@ -104,6 +107,22 @@ static int event_input_timer(struct snd_seq_event * ev, int direct, void *privat return snd_seq_control_queue(ev, atomic, hop); } +static int sys_announce_subscribe(void *private_data, + struct snd_seq_port_subscribe *info) +{ + announce_subscribed++; + return 0; +} + +static int sys_announce_unsubscribe(void *private_data, + struct snd_seq_port_subscribe *info) +{ + if (snd_BUG_ON(!announce_subscribed)) + return 0; + announce_subscribed--; + return 0; +} + /* register our internal client */ int __init snd_seq_system_client_init(void) { @@ -143,7 +162,10 @@ int __init snd_seq_system_client_init(void) /* register announcement port */ strcpy(port->name, "Announce"); port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ - port->kernel = NULL; + pcallbacks.event_input = NULL; + pcallbacks.subscribe = sys_announce_subscribe; + pcallbacks.unsubscribe = sys_announce_unsubscribe; + port->kernel = &pcallbacks; port->type = 0; port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; port->addr.client = sysclient; diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h index 4fe88ad40346..62e513f74871 100644 --- a/sound/core/seq/seq_system.h +++ b/sound/core/seq/seq_system.h @@ -10,16 +10,31 @@ /* entry points for broadcasting system events */ -void snd_seq_system_broadcast(int client, int port, int type); - -#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) -#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) -#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) -#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START) -#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) -#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) - -int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev); +void snd_seq_system_broadcast(int client, int port, int type, bool atomic); + +/* normal system notification event broadcast */ +#define notify_event(client, port, type) \ + snd_seq_system_broadcast(client, port, type, false) + +/* notify UMP EP/FB change event */ +static inline void snd_seq_system_ump_notify(int client, int block, int type, + bool atomic) +{ + /* reuse the existing snd_seq_system_broadcast(): + * struct snd_seq_ev_ump_notify is compatible with struct snd_seq_addr + */ + snd_seq_system_broadcast(client, block, type, atomic); +} + +#define snd_seq_system_client_ev_client_start(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) +#define snd_seq_system_client_ev_client_exit(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) +#define snd_seq_system_client_ev_client_change(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) +#define snd_seq_system_client_ev_port_start(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_START) +#define snd_seq_system_client_ev_port_exit(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) +#define snd_seq_system_client_ev_port_change(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) + +int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev, + bool atomic); /* register our internal client */ int snd_seq_system_client_init(void); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index ad2b97e2762d..c9f0392ac7f1 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -20,14 +20,17 @@ static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr) { - if (tmr->tempo < 1000000) - tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq; + unsigned int threshold = + tmr->tempo_base == 1000 ? 1000000 : 10000; + + if (tmr->tempo < threshold) + tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq; else { /* might overflow.. */ unsigned int s; s = tmr->tempo % tmr->ppq; - s = (s * 1000) / tmr->ppq; - tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000; + s = (s * tmr->tempo_base) / tmr->ppq; + tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base; tmr->tick.resolution += s; } if (tmr->tick.resolution <= 0) @@ -79,6 +82,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) /* setup defaults */ tmr->ppq = 96; /* 96 PPQ */ tmr->tempo = 500000; /* 120 BPM */ + tmr->tempo_base = 1000; /* 1us */ snd_seq_timer_set_tick_resolution(tmr); tmr->running = 0; @@ -164,8 +168,9 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) return 0; } -/* set current tempo and ppq in a shot */ -int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) +/* set current tempo, ppq and base in a shot */ +int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq, + unsigned int tempo_base) { int changed; @@ -173,6 +178,9 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) return -EINVAL; if (tempo <= 0 || ppq <= 0) return -EINVAL; + /* allow only 10ns or 1us tempo base for now */ + if (tempo_base && tempo_base != 10 && tempo_base != 1000) + return -EINVAL; guard(spinlock_irqsave)(&tmr->lock); if (tmr->running && (ppq != tmr->ppq)) { /* refuse to change ppq on running timers */ @@ -183,6 +191,7 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) changed = (tempo != tmr->tempo) || (ppq != tmr->ppq); tmr->tempo = tempo; tmr->ppq = ppq; + tmr->tempo_base = tempo_base ? tempo_base : 1000; if (changed) snd_seq_timer_set_tick_resolution(tmr); return 0; diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h index 4bec57df8158..c8803216a3a4 100644 --- a/sound/core/seq/seq_timer.h +++ b/sound/core/seq/seq_timer.h @@ -36,6 +36,7 @@ struct snd_seq_timer { unsigned int skew; unsigned int skew_base; + unsigned int tempo_base; struct timespec64 last_update; /* time of last clock update, used for interpolation */ @@ -108,15 +109,14 @@ static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long struct snd_seq_queue; int snd_seq_timer_open(struct snd_seq_queue *q); int snd_seq_timer_close(struct snd_seq_queue *q); -int snd_seq_timer_midi_open(struct snd_seq_queue *q); -int snd_seq_timer_midi_close(struct snd_seq_queue *q); void snd_seq_timer_defaults(struct snd_seq_timer *tmr); void snd_seq_timer_reset(struct snd_seq_timer *tmr); int snd_seq_timer_stop(struct snd_seq_timer *tmr); int snd_seq_timer_start(struct snd_seq_timer *tmr); int snd_seq_timer_continue(struct snd_seq_timer *tmr); int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo); -int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq); +int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq, + unsigned int tempo_base); int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position); int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position); int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base); diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index c627d72f7fe2..1255351b59ce 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -23,14 +23,6 @@ enum { STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT }; -/* object per UMP group; corresponding to a sequencer port */ -struct seq_ump_group { - int group; /* group index (0-based) */ - unsigned int dir_bits; /* directions */ - bool active; /* activeness */ - char name[64]; /* seq port name */ -}; - /* context for UMP input parsing, per EP */ struct seq_ump_input_buffer { unsigned char len; /* total length in words */ @@ -47,7 +39,6 @@ struct seq_ump_client { int opened[2]; /* current opens for each direction */ struct snd_rawmidi_file out_rfile; /* rawmidi for output */ struct seq_ump_input_buffer input; /* input parser context */ - struct seq_ump_group groups[SNDRV_UMP_MAX_GROUPS]; /* table of groups */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ struct work_struct group_notify_work; /* FB change notification */ }; @@ -174,7 +165,7 @@ static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info) /* fill port_info from the given UMP EP and group info */ static void fill_port_info(struct snd_seq_port_info *port, struct seq_ump_client *client, - struct seq_ump_group *group) + struct snd_ump_group *group) { unsigned int rawmidi_info = client->ump->core.info_flags; @@ -198,6 +189,8 @@ static void fill_port_info(struct snd_seq_port_info *port, port->ump_group = group->group + 1; if (!group->active) port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE; + if (group->is_midi1) + port->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1; port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | SNDRV_SEQ_PORT_TYPE_MIDI_UMP | SNDRV_SEQ_PORT_TYPE_HARDWARE | @@ -210,19 +203,29 @@ static void fill_port_info(struct snd_seq_port_info *port, sprintf(port->name, "Group %d", group->group + 1); } +/* skip non-existing group for static blocks */ +static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *group) +{ + return !group->valid && + (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS); +} + /* create a new sequencer port per UMP group */ static int seq_ump_group_init(struct seq_ump_client *client, int group_index) { - struct seq_ump_group *group = &client->groups[group_index]; + struct snd_ump_group *group = &client->ump->groups[group_index]; struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback pcallbacks; + if (skip_group(client, group)) + return 0; + port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; fill_port_info(port, client, group); - port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port->flags |= SNDRV_SEQ_PORT_FLG_GIVEN_PORT; memset(&pcallbacks, 0, sizeof(pcallbacks)); pcallbacks.owner = THIS_MODULE; pcallbacks.private_data = client; @@ -250,14 +253,17 @@ static void update_port_infos(struct seq_ump_client *client) return; for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { + if (skip_group(client, &client->ump->groups[i])) + continue; + old->addr.client = client->seq_client; - old->addr.port = i; + old->addr.port = ump_group_to_seq_port(i); err = snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_GET_PORT_INFO, old); if (err < 0) - return; - fill_port_info(new, client, &client->groups[i]); + continue; + fill_port_info(new, client, &client->ump->groups[i]); if (old->capability == new->capability && !strcmp(old->name, new->name)) continue; @@ -265,58 +271,7 @@ static void update_port_infos(struct seq_ump_client *client) SNDRV_SEQ_IOCTL_SET_PORT_INFO, new); if (err < 0) - return; - /* notify to system port */ - snd_seq_system_client_ev_port_change(client->seq_client, i); - } -} - -/* update dir_bits and active flag for all groups in the client */ -static void update_group_attrs(struct seq_ump_client *client) -{ - struct snd_ump_block *fb; - struct seq_ump_group *group; - int i; - - for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { - group = &client->groups[i]; - *group->name = 0; - group->dir_bits = 0; - group->active = 0; - group->group = i; - } - - list_for_each_entry(fb, &client->ump->block_list, list) { - if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS) - break; - group = &client->groups[fb->info.first_group]; - for (i = 0; i < fb->info.num_groups; i++, group++) { - if (fb->info.active) - group->active = 1; - switch (fb->info.direction) { - case SNDRV_UMP_DIR_INPUT: - group->dir_bits |= (1 << STR_IN); - break; - case SNDRV_UMP_DIR_OUTPUT: - group->dir_bits |= (1 << STR_OUT); - break; - case SNDRV_UMP_DIR_BIDIRECTION: - group->dir_bits |= (1 << STR_OUT) | (1 << STR_IN); - break; - } - if (!*fb->info.name) - continue; - if (!*group->name) { - /* store the first matching name */ - strscpy(group->name, fb->info.name, - sizeof(group->name)); - } else { - /* when overlapping, concat names */ - strlcat(group->name, ", ", sizeof(group->name)); - strlcat(group->name, fb->info.name, - sizeof(group->name)); - } - } + continue; } } @@ -416,7 +371,7 @@ static void setup_client_group_filter(struct seq_ump_client *client) return; filter = ~(1U << 0); /* always allow groupless messages */ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { - if (client->groups[p].active) + if (client->ump->groups[p].active) filter &= ~(1U << (p + 1)); } cptr->group_filter = filter; @@ -429,11 +384,37 @@ static void handle_group_notify(struct work_struct *work) struct seq_ump_client *client = container_of(work, struct seq_ump_client, group_notify_work); - update_group_attrs(client); update_port_infos(client); setup_client_group_filter(client); } +/* UMP EP change notification */ +static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump) +{ + struct seq_ump_client *client = ump->seq_client; + struct snd_seq_client *cptr; + int client_id; + + if (!client) + return -ENODEV; + client_id = client->seq_client; + cptr = snd_seq_kernel_client_get(client_id); + if (!cptr) + return -ENODEV; + + snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + true); + + /* update sequencer client name if needed */ + if (*ump->core.name && strcmp(ump->core.name, cptr->name)) { + strscpy(cptr->name, ump->core.name, sizeof(cptr->name)); + snd_seq_system_client_ev_client_change(client_id); + } + + snd_seq_kernel_client_put(cptr); + return 0; +} + /* UMP FB change notification */ static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, struct snd_ump_block *fb) @@ -443,20 +424,29 @@ static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, if (!client) return -ENODEV; schedule_work(&client->group_notify_work); + snd_seq_system_ump_notify(client->seq_client, fb->info.block_id, + SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE, + true); return 0; } /* UMP protocol change notification; just update the midi_version field */ static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) { - if (!ump->seq_client) + struct seq_ump_client *client = ump->seq_client; + + if (!client) return -ENODEV; - setup_client_midi_version(ump->seq_client); + setup_client_midi_version(client); + snd_seq_system_ump_notify(client->seq_client, 0, + SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + true); return 0; } static const struct snd_seq_ump_ops seq_ump_ops = { .input_receive = seq_ump_input_receive, + .notify_ep_change = seq_ump_notify_ep_change, .notify_fb_change = seq_ump_notify_fb_change, .switch_protocol = seq_ump_switch_protocol, }; @@ -492,7 +482,6 @@ static int snd_seq_ump_probe(struct device *_dev) client->ump_info[fb->info.block_id + 1] = &fb->info; setup_client_midi_version(client); - update_group_attrs(client); for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { err = seq_ump_group_init(client, p); diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c index ee6ac649df83..ff7e558b4d51 100644 --- a/sound/core/seq/seq_ump_convert.c +++ b/sound/core/seq/seq_ump_convert.c @@ -157,7 +157,7 @@ static void ump_system_to_one_param_ev(const union snd_ump_midi1_msg *val, static void ump_system_to_songpos_ev(const union snd_ump_midi1_msg *val, struct snd_seq_event *ev) { - ev->data.control.value = (val->system.parm1 << 7) | val->system.parm2; + ev->data.control.value = (val->system.parm2 << 7) | val->system.parm1; } /* Encoders for 0xf0 - 0xff */ @@ -368,6 +368,7 @@ static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest, struct snd_seq_ump_event ev_cvt; const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump; union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump; + struct ump_cvt_to_ump_bank *cc; ev_cvt = *event; memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump)); @@ -387,11 +388,29 @@ static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest, midi2->paf.data = upscale_7_to_32bit(midi1->paf.data); break; case UMP_MSG_STATUS_CC: + cc = &dest_port->midi2_bank[midi1->note.channel]; + switch (midi1->cc.index) { + case UMP_CC_BANK_SELECT: + cc->bank_set = 1; + cc->cc_bank_msb = midi1->cc.data; + return 0; // skip + case UMP_CC_BANK_SELECT_LSB: + cc->bank_set = 1; + cc->cc_bank_lsb = midi1->cc.data; + return 0; // skip + } midi2->cc.index = midi1->cc.index; midi2->cc.data = upscale_7_to_32bit(midi1->cc.data); break; case UMP_MSG_STATUS_PROGRAM: midi2->pg.program = midi1->pg.program; + cc = &dest_port->midi2_bank[midi1->note.channel]; + if (cc->bank_set) { + midi2->pg.bank_valid = 1; + midi2->pg.bank_msb = cc->cc_bank_msb; + midi2->pg.bank_lsb = cc->cc_bank_lsb; + cc->bank_set = 0; + } break; case UMP_MSG_STATUS_CHANNEL_PRESSURE: midi2->caf.data = upscale_7_to_32bit(midi1->caf.data); @@ -419,6 +438,7 @@ static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest, struct snd_seq_ump_event ev_cvt; union snd_ump_midi1_msg *midi1 = (union snd_ump_midi1_msg *)ev_cvt.ump; const union snd_ump_midi2_msg *midi2 = (const union snd_ump_midi2_msg *)event->ump; + int err; u16 v; ev_cvt = *event; @@ -443,6 +463,24 @@ static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest, midi1->cc.data = downscale_32_to_7bit(midi2->cc.data); break; case UMP_MSG_STATUS_PROGRAM: + if (midi2->pg.bank_valid) { + midi1->cc.status = UMP_MSG_STATUS_CC; + midi1->cc.index = UMP_CC_BANK_SELECT; + midi1->cc.data = midi2->pg.bank_msb; + err = __snd_seq_deliver_single_event(dest, dest_port, + (struct snd_seq_event *)&ev_cvt, + atomic, hop); + if (err < 0) + return err; + midi1->cc.index = UMP_CC_BANK_SELECT_LSB; + midi1->cc.data = midi2->pg.bank_lsb; + err = __snd_seq_deliver_single_event(dest, dest_port, + (struct snd_seq_event *)&ev_cvt, + atomic, hop); + if (err < 0) + return err; + midi1->note.status = midi2->note.status; + } midi1->pg.program = midi2->pg.program; break; case UMP_MSG_STATUS_CHANNEL_PRESSURE: @@ -557,12 +595,13 @@ int snd_seq_deliver_from_ump(struct snd_seq_client *source, type = ump_message_type(ump_ev->ump[0]); if (snd_seq_client_is_ump(dest)) { - if (snd_seq_client_is_midi2(dest) && - type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE) + bool is_midi2 = snd_seq_client_is_midi2(dest) && + !dest_port->is_midi1; + + if (is_midi2 && type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE) return cvt_ump_midi1_to_midi2(dest, dest_port, event, atomic, hop); - else if (!snd_seq_client_is_midi2(dest) && - type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE) + else if (!is_midi2 && type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE) return cvt_ump_midi2_to_midi1(dest, dest_port, event, atomic, hop); /* non-EP port and different group is set? */ @@ -691,6 +730,7 @@ static int system_ev_to_ump_midi1(const struct snd_seq_event *event, union snd_ump_midi1_msg *data, unsigned char status) { + data->system.type = UMP_MSG_TYPE_SYSTEM; // override data->system.status = status; return 1; } @@ -701,6 +741,7 @@ static int system_1p_ev_to_ump_midi1(const struct snd_seq_event *event, union snd_ump_midi1_msg *data, unsigned char status) { + data->system.type = UMP_MSG_TYPE_SYSTEM; // override data->system.status = status; data->system.parm1 = event->data.control.value & 0x7f; return 1; @@ -712,9 +753,10 @@ static int system_2p_ev_to_ump_midi1(const struct snd_seq_event *event, union snd_ump_midi1_msg *data, unsigned char status) { + data->system.type = UMP_MSG_TYPE_SYSTEM; // override data->system.status = status; - data->system.parm1 = (event->data.control.value >> 7) & 0x7f; - data->system.parm2 = event->data.control.value & 0x7f; + data->system.parm1 = event->data.control.value & 0x7f; + data->system.parm2 = (event->data.control.value >> 7) & 0x7f; return 1; } @@ -748,26 +790,45 @@ static int paf_ev_to_ump_midi2(const struct snd_seq_event *event, return 1; } +static void reset_rpn(struct ump_cvt_to_ump_bank *cc) +{ + cc->rpn_set = 0; + cc->nrpn_set = 0; + cc->cc_rpn_msb = cc->cc_rpn_lsb = 0; + cc->cc_data_msb = cc->cc_data_lsb = 0; + cc->cc_data_msb_set = cc->cc_data_lsb_set = 0; +} + /* set up the MIDI2 RPN/NRPN packet data from the parsed info */ -static void fill_rpn(struct snd_seq_ump_midi2_bank *cc, - union snd_ump_midi2_msg *data) +static int fill_rpn(struct ump_cvt_to_ump_bank *cc, + union snd_ump_midi2_msg *data, + unsigned char channel, + bool flush) { + if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set)) + return 0; // skip + /* when not flushing, wait for complete data set */ + if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set)) + return 0; // skip + if (cc->rpn_set) { data->rpn.status = UMP_MSG_STATUS_RPN; data->rpn.bank = cc->cc_rpn_msb; data->rpn.index = cc->cc_rpn_lsb; - cc->rpn_set = 0; - cc->cc_rpn_msb = cc->cc_rpn_lsb = 0; - } else { + } else if (cc->nrpn_set) { data->rpn.status = UMP_MSG_STATUS_NRPN; data->rpn.bank = cc->cc_nrpn_msb; data->rpn.index = cc->cc_nrpn_lsb; - cc->nrpn_set = 0; - cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0; + } else { + return 0; // skip } + data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) | cc->cc_data_lsb); - cc->cc_data_msb = cc->cc_data_lsb = 0; + data->rpn.channel = channel; + + reset_rpn(cc); + return 1; } /* convert CC event to MIDI 2.0 UMP */ @@ -779,29 +840,39 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event, unsigned char channel = event->data.control.channel & 0x0f; unsigned char index = event->data.control.param & 0x7f; unsigned char val = event->data.control.value & 0x7f; - struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; + struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel]; + int ret; /* process special CC's (bank/rpn/nrpn) */ switch (index) { case UMP_CC_RPN_MSB: + ret = fill_rpn(cc, data, channel, true); cc->rpn_set = 1; cc->cc_rpn_msb = val; - return 0; // skip + if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) + reset_rpn(cc); + return ret; case UMP_CC_RPN_LSB: + ret = fill_rpn(cc, data, channel, true); cc->rpn_set = 1; cc->cc_rpn_lsb = val; - return 0; // skip + if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) + reset_rpn(cc); + return ret; case UMP_CC_NRPN_MSB: + ret = fill_rpn(cc, data, channel, true); cc->nrpn_set = 1; cc->cc_nrpn_msb = val; - return 0; // skip + return ret; case UMP_CC_NRPN_LSB: + ret = fill_rpn(cc, data, channel, true); cc->nrpn_set = 1; cc->cc_nrpn_lsb = val; - return 0; // skip + return ret; case UMP_CC_DATA: + cc->cc_data_msb_set = 1; cc->cc_data_msb = val; - return 0; // skip + return fill_rpn(cc, data, channel, false); case UMP_CC_BANK_SELECT: cc->bank_set = 1; cc->cc_bank_msb = val; @@ -811,11 +882,9 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event, cc->cc_bank_lsb = val; return 0; // skip case UMP_CC_DATA_LSB: + cc->cc_data_lsb_set = 1; cc->cc_data_lsb = val; - if (!(cc->rpn_set || cc->nrpn_set)) - return 0; // skip - fill_rpn(cc, data); - return 1; + return fill_rpn(cc, data, channel, false); } data->cc.status = status; @@ -844,7 +913,7 @@ static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event, unsigned char status) { unsigned char channel = event->data.control.channel & 0x0f; - struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; + struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel]; data->pg.status = status; data->pg.channel = channel; @@ -854,7 +923,6 @@ static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event, data->pg.bank_msb = cc->cc_bank_msb; data->pg.bank_lsb = cc->cc_bank_lsb; cc->bank_set = 0; - cc->cc_bank_msb = cc->cc_bank_lsb = 0; } return 1; } @@ -882,8 +950,9 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event, { unsigned char channel = event->data.control.channel & 0x0f; unsigned char index = event->data.control.param & 0x7f; - struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; + struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel]; unsigned char msb, lsb; + int ret; msb = (event->data.control.value >> 7) & 0x7f; lsb = event->data.control.value & 0x7f; @@ -897,28 +966,27 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event, cc->cc_bank_lsb = lsb; return 0; // skip case UMP_CC_RPN_MSB: - cc->cc_rpn_msb = msb; - fallthrough; case UMP_CC_RPN_LSB: - cc->rpn_set = 1; + ret = fill_rpn(cc, data, channel, true); + cc->cc_rpn_msb = msb; cc->cc_rpn_lsb = lsb; - return 0; // skip + cc->rpn_set = 1; + if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) + reset_rpn(cc); + return ret; case UMP_CC_NRPN_MSB: - cc->cc_nrpn_msb = msb; - fallthrough; case UMP_CC_NRPN_LSB: + ret = fill_rpn(cc, data, channel, true); + cc->cc_nrpn_msb = msb; cc->nrpn_set = 1; cc->cc_nrpn_lsb = lsb; - return 0; // skip + return ret; case UMP_CC_DATA: - cc->cc_data_msb = msb; - fallthrough; case UMP_CC_DATA_LSB: + cc->cc_data_msb_set = cc->cc_data_lsb_set = 1; + cc->cc_data_msb = msb; cc->cc_data_lsb = lsb; - if (!(cc->rpn_set || cc->nrpn_set)) - return 0; // skip - fill_rpn(cc, data); - return 1; + return fill_rpn(cc, data, channel, false); } data->cc.status = UMP_MSG_STATUS_CC; @@ -978,7 +1046,7 @@ static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event, union snd_ump_midi2_msg *data, unsigned char status) { - return system_1p_ev_to_ump_midi1(event, dest_port, + return system_2p_ev_to_ump_midi1(event, dest_port, (union snd_ump_midi1_msg *)data, status); } @@ -1035,6 +1103,8 @@ static const struct seq_ev_to_ump seq_ev_ump_encoders[] = { system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, { SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING, system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, + { SNDRV_SEQ_EVENT_RESET, UMP_SYSTEM_STATUS_RESET, + system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, }; static const struct seq_ev_to_ump *find_ump_encoder(int type) @@ -1148,44 +1218,53 @@ static int cvt_sysex_to_ump(struct snd_seq_client *dest, { struct snd_seq_ump_event ev_cvt; unsigned char status; - u8 buf[6], *xbuf; + u8 buf[8], *xbuf; int offset = 0; int len, err; + bool finished = false; if (!snd_seq_ev_is_variable(event)) return 0; setup_ump_event(&ev_cvt, event); - for (;;) { + while (!finished) { len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset); if (len <= 0) break; - if (WARN_ON(len > 6)) + if (WARN_ON(len > sizeof(buf))) break; - offset += len; + xbuf = buf; + status = UMP_SYSEX_STATUS_CONTINUE; + /* truncate the sysex start-marker */ if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) { status = UMP_SYSEX_STATUS_START; - xbuf++; len--; - if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) { + offset++; + xbuf++; + } + + /* if the last of this packet or the 1st byte of the next packet + * is the end-marker, finish the transfer with this packet + */ + if (len > 0 && len < 8 && + xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) { + if (status == UMP_SYSEX_STATUS_START) status = UMP_SYSEX_STATUS_SINGLE; - len--; - } - } else { - if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) { + else status = UMP_SYSEX_STATUS_END; - len--; - } else { - status = UMP_SYSEX_STATUS_CONTINUE; - } + len--; + finished = true; } + + len = min(len, 6); fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len); err = __snd_seq_deliver_single_event(dest, dest_port, (struct snd_seq_event *)&ev_cvt, atomic, hop); if (err < 0) return err; + offset += len; } return 0; } @@ -1201,7 +1280,7 @@ int snd_seq_deliver_to_ump(struct snd_seq_client *source, return 0; /* group filtered - skip the event */ if (event->type == SNDRV_SEQ_EVENT_SYSEX) return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop); - else if (snd_seq_client_is_midi2(dest)) + else if (snd_seq_client_is_midi2(dest) && !dest_port->is_midi1) return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop); else return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop); diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index 654d620d0199..4492be5d2317 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -40,7 +40,7 @@ MODULE_LICENSE("GPL"); /* * bus definition */ -static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) +static int snd_seq_bus_match(struct device *dev, const struct device_driver *drv) { struct snd_seq_device *sdev = to_seq_dev(dev); struct snd_seq_driver *sdrv = to_seq_drv(drv); @@ -234,7 +234,7 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id, put_device(&dev->dev); return err; } - + if (result) *result = dev; diff --git a/sound/core/sound.c b/sound/core/sound.c index b9db9aa0bfcb..6531a67f13b3 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -133,7 +133,7 @@ static struct snd_minor *autoload_device(unsigned int minor) /* /dev/aloadSEQ */ snd_request_other(minor); } - mutex_lock(&sound_mutex); /* reacuire lock */ + mutex_lock(&sound_mutex); /* reacquire lock */ return snd_minors[minor]; } #else /* !CONFIG_MODULES */ diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index eb90f62228c0..84e337ecbddd 100644 --- a/sound/core/sound_kunit.c +++ b/sound/core/sound_kunit.c @@ -45,7 +45,7 @@ struct avail_test_data { snd_pcm_uframes_t expected_avail; }; -static struct snd_format_test_data valid_fmt[] = { +static const struct snd_format_test_data valid_fmt[] = { DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()), DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)), DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()), @@ -154,7 +154,7 @@ static void test_format_endianness(struct kunit *test) KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL); } -static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *data, +static void _test_fill_silence(struct kunit *test, const struct snd_format_test_data *data, u8 *buffer, size_t samples_count) { size_t sample_bytes = data->physical_bits >> 3; @@ -167,11 +167,12 @@ static void _test_fill_silence(struct kunit *test, struct snd_format_test_data * static void test_format_fill_silence(struct kunit *test) { - u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES }; + static const u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES }; u8 *buffer; u32 i, j; buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); for (i = 0; i < ARRAY_SIZE(buf_samples); i++) { for (j = 0; j < ARRAY_SIZE(valid_fmt); j++) @@ -191,7 +192,7 @@ static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size) return boundary; } -static struct avail_test_data p_avail_data[] = { +static const struct avail_test_data p_avail_data[] = { /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */ { 128, 1000, 1129, 1073741824UL - 1 }, /* @@ -208,8 +209,12 @@ static void test_playback_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) { r->buffer_size = p_avail_data[i].buffer_size; @@ -220,7 +225,7 @@ static void test_playback_avail(struct kunit *test) } } -static struct avail_test_data c_avail_data[] = { +static const struct avail_test_data c_avail_data[] = { /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */ { 128, 1000, 1001, 1073741824UL - 1 }, /* standard case: avail = hw_ptr - appl_ptr */ @@ -232,8 +237,12 @@ static void test_capture_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) { r->buffer_size = c_avail_data[i].buffer_size; @@ -247,6 +256,7 @@ static void test_capture_avail(struct kunit *test) static void test_card_set_id(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_card_set_id(card, VALID_NAME); KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME); @@ -280,6 +290,7 @@ static void test_pcm_format_name(struct kunit *test) static void test_card_add_component(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_component_add(card, TEST_FIRST_COMPONENT); KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT); @@ -308,5 +319,6 @@ static struct kunit_suite sound_utils_suite = { }; kunit_test_suite(sound_utils_suite); +MODULE_DESCRIPTION("Sound core KUnit test"); MODULE_AUTHOR("Ivan Orlov"); MODULE_LICENSE("GPL"); diff --git a/sound/core/timer.c b/sound/core/timer.c index 4d2ee99c12a3..fbada79380f9 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -13,6 +13,8 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/sched/signal.h> +#include <linux/anon_inodes.h> +#include <linux/idr.h> #include <sound/core.h> #include <sound/timer.h> #include <sound/control.h> @@ -109,6 +111,16 @@ struct snd_timer_status64 { unsigned char reserved[64]; /* reserved */ }; +#ifdef CONFIG_SND_UTIMER +#define SNDRV_UTIMERS_MAX_COUNT 128 +/* Internal data structure for keeping the state of the userspace-driven timer */ +struct snd_utimer { + char *name; + struct snd_timer *timer; + unsigned int id; +}; +#endif + #define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64) /* list of timers */ @@ -544,6 +556,14 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, SNDRV_TIMER_IFLG_START)) return -EBUSY; + /* check the actual time for the start tick; + * bail out as error if it's way too low (< 100us) + */ + if (start && !(timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) { + if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000) + return -EINVAL; + } + if (start) timeri->ticks = timeri->cticks = ticks; else if (!timeri->cticks) @@ -1154,7 +1174,7 @@ static int snd_timer_s_close(struct snd_timer *timer) static const struct snd_timer_hardware snd_timer_system = { .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, - .resolution = 1000000000L / HZ, + .resolution = NSEC_PER_SEC / HZ, .ticks = 10000000L, .close = snd_timer_s_close, .start = snd_timer_s_start, @@ -1595,7 +1615,7 @@ static int snd_timer_user_ginfo(struct file *file, ginfo = memdup_user(_ginfo, sizeof(*ginfo)); if (IS_ERR(ginfo)) - return PTR_ERR(no_free_ptr(ginfo)); + return PTR_ERR(ginfo); tid = ginfo->tid; memset(ginfo, 0, sizeof(*ginfo)); @@ -2001,6 +2021,217 @@ enum { SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), }; +#ifdef CONFIG_SND_UTIMER +/* + * Since userspace-driven timers are passed to userspace, we need to have an identifier + * which will allow us to use them (basically, the subdevice number of udriven timer). + */ +static DEFINE_IDA(snd_utimer_ids); + +static void snd_utimer_put_id(struct snd_utimer *utimer) +{ + int timer_id = utimer->id; + + snd_BUG_ON(timer_id < 0 || timer_id >= SNDRV_UTIMERS_MAX_COUNT); + ida_free(&snd_utimer_ids, timer_id); +} + +static int snd_utimer_take_id(void) +{ + return ida_alloc_max(&snd_utimer_ids, SNDRV_UTIMERS_MAX_COUNT - 1, GFP_KERNEL); +} + +static void snd_utimer_free(struct snd_utimer *utimer) +{ + snd_timer_free(utimer->timer); + snd_utimer_put_id(utimer); + kfree(utimer->name); + kfree(utimer); +} + +static int snd_utimer_release(struct inode *inode, struct file *file) +{ + struct snd_utimer *utimer = (struct snd_utimer *)file->private_data; + + snd_utimer_free(utimer); + return 0; +} + +static int snd_utimer_trigger(struct file *file) +{ + struct snd_utimer *utimer = (struct snd_utimer *)file->private_data; + + snd_timer_interrupt(utimer->timer, utimer->timer->sticks); + return 0; +} + +static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) +{ + switch (ioctl) { + case SNDRV_TIMER_IOCTL_TRIGGER: + return snd_utimer_trigger(file); + } + + return -ENOTTY; +} + +static const struct file_operations snd_utimer_fops = { + .llseek = noop_llseek, + .release = snd_utimer_release, + .unlocked_ioctl = snd_utimer_ioctl, +}; + +static int snd_utimer_start(struct snd_timer *t) +{ + return 0; +} + +static int snd_utimer_stop(struct snd_timer *t) +{ + return 0; +} + +static int snd_utimer_open(struct snd_timer *t) +{ + return 0; +} + +static int snd_utimer_close(struct snd_timer *t) +{ + return 0; +} + +static const struct snd_timer_hardware timer_hw = { + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK, + .open = snd_utimer_open, + .close = snd_utimer_close, + .start = snd_utimer_start, + .stop = snd_utimer_stop, +}; + +static int snd_utimer_create(struct snd_timer_uinfo *utimer_info, + struct snd_utimer **r_utimer) +{ + struct snd_utimer *utimer; + struct snd_timer *timer; + struct snd_timer_id tid; + int utimer_id; + int err = 0; + + if (!utimer_info || utimer_info->resolution == 0) + return -EINVAL; + + utimer = kzalloc(sizeof(*utimer), GFP_KERNEL); + if (!utimer) + return -ENOMEM; + + /* We hold the ioctl lock here so we won't get a race condition when allocating id */ + utimer_id = snd_utimer_take_id(); + if (utimer_id < 0) { + err = utimer_id; + goto err_take_id; + } + + utimer->name = kasprintf(GFP_KERNEL, "snd-utimer%d", utimer_id); + if (!utimer->name) { + err = -ENOMEM; + goto err_get_name; + } + + utimer->id = utimer_id; + + tid.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.card = -1; + tid.device = SNDRV_TIMER_GLOBAL_UDRIVEN; + tid.subdevice = utimer_id; + + err = snd_timer_new(NULL, utimer->name, &tid, &timer); + if (err < 0) { + pr_err("Can't create userspace-driven timer\n"); + goto err_timer_new; + } + + timer->module = THIS_MODULE; + timer->hw = timer_hw; + timer->hw.resolution = utimer_info->resolution; + timer->hw.ticks = 1; + timer->max_instances = MAX_SLAVE_INSTANCES; + + utimer->timer = timer; + + err = snd_timer_global_register(timer); + if (err < 0) { + pr_err("Can't register a userspace-driven timer\n"); + goto err_timer_reg; + } + + *r_utimer = utimer; + return 0; + +err_timer_reg: + snd_timer_free(timer); +err_timer_new: + kfree(utimer->name); +err_get_name: + snd_utimer_put_id(utimer); +err_take_id: + kfree(utimer); + + return err; +} + +static int snd_utimer_ioctl_create(struct file *file, + struct snd_timer_uinfo __user *_utimer_info) +{ + struct snd_utimer *utimer; + struct snd_timer_uinfo *utimer_info __free(kfree) = NULL; + int err, timer_fd; + + utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info)); + if (IS_ERR(utimer_info)) + return PTR_ERR(utimer_info); + + err = snd_utimer_create(utimer_info, &utimer); + if (err < 0) + return err; + + utimer_info->id = utimer->id; + + timer_fd = anon_inode_getfd(utimer->name, &snd_utimer_fops, utimer, O_RDWR | O_CLOEXEC); + if (timer_fd < 0) { + snd_utimer_free(utimer); + return timer_fd; + } + + utimer_info->fd = timer_fd; + + err = copy_to_user(_utimer_info, utimer_info, sizeof(*utimer_info)); + if (err) { + /* + * "Leak" the fd, as there is nothing we can do about it. + * It might have been closed already since anon_inode_getfd + * makes it available for userspace. + * + * We have to rely on the process exit path to do any + * necessary cleanup (e.g. releasing the file). + */ + return -EFAULT; + } + + return 0; +} + +#else + +static int snd_utimer_ioctl_create(struct file *file, + struct snd_timer_uinfo __user *_utimer_info) +{ + return -ENOTTY; +} + +#endif + static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg, bool compat) { @@ -2045,6 +2276,8 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, case SNDRV_TIMER_IOCTL_PAUSE: case SNDRV_TIMER_IOCTL_PAUSE_OLD: return snd_timer_user_pause(file); + case SNDRV_TIMER_IOCTL_CREATE: + return snd_utimer_ioctl_create(file, argp); } return -ENOTTY; } @@ -2203,7 +2436,6 @@ static const struct file_operations snd_timer_f_ops = .read = snd_timer_user_read, .open = snd_timer_user_open, .release = snd_timer_user_release, - .llseek = no_llseek, .poll = snd_timer_user_poll, .unlocked_ioctl = snd_timer_user_ioctl, .compat_ioctl = snd_timer_user_ioctl_compat, diff --git a/sound/core/ump.c b/sound/core/ump.c index fd6a68a54278..8d8681a42ca5 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -37,6 +37,8 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, u32 *buffer, int count); static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words); +static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump); +static void update_legacy_names(struct snd_ump_endpoint *ump); #else static inline int process_legacy_output(struct snd_ump_endpoint *ump, u32 *buffer, int count) @@ -47,8 +49,42 @@ static inline void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words) { } +static inline void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ +} +static inline void update_legacy_names(struct snd_ump_endpoint *ump) +{ +} #endif +/* copy a string safely with stripping non-printable letters */ +static void safe_copy_string(void *dst, size_t max_dst_size, + const void *src, size_t max_src_size) +{ + const unsigned char *s = src; + unsigned char *d = dst; + + if (!max_dst_size--) + return; + for (s = src; max_dst_size && *s && max_src_size--; s++) { + if (!isascii(*s) || !isprint(*s)) + continue; + *d++ = *s; + max_dst_size--; + } + *d = 0; +} + +/* append a string safely with stripping non-printable letters */ +static void safe_append_string(void *dst, size_t max_dst_size, + const void *src, size_t max_src_size) +{ + unsigned char *d = dst; + size_t len = strlen(d); + + safe_copy_string(d + len, max_dst_size - len, src, max_src_size); +} + static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = { .dev_register = snd_ump_dev_register, .dev_unregister = snd_ump_dev_unregister, @@ -366,7 +402,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, { struct snd_ump_block *fb, *p; - if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS) + if (blk >= SNDRV_UMP_MAX_BLOCKS) return -EINVAL; if (snd_ump_get_block(ump, blk)) @@ -387,7 +423,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, fb->info.first_group = first_group; fb->info.num_groups = num_groups; /* fill the default name, may be overwritten to a better name */ - snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d", + snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u", first_group + 1, first_group + num_groups); /* put the entry in the ordered list */ @@ -489,11 +525,7 @@ static void snd_ump_proc_read(struct snd_info_entry *entry, ump->info.manufacturer_id); snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id); snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id); - snd_iprintf(buffer, "SW Revision: 0x%02x%02x%02x%02x\n", - ump->info.sw_revision[0], - ump->info.sw_revision[1], - ump->info.sw_revision[2], - ump->info.sw_revision[3]); + snd_iprintf(buffer, "SW Revision: 0x%4phN\n", ump->info.sw_revision); } snd_iprintf(buffer, "Static Blocks: %s\n", (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No"); @@ -524,6 +556,56 @@ static void snd_ump_proc_read(struct snd_info_entry *entry, } } +/* update dir_bits and active flag for all groups in the client */ +void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) +{ + struct snd_ump_block *fb; + struct snd_ump_group *group; + int i; + + for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { + group = &ump->groups[i]; + *group->name = 0; + group->dir_bits = 0; + group->active = 0; + group->group = i; + group->valid = false; + group->is_midi1 = false; + } + + list_for_each_entry(fb, &ump->block_list, list) { + if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS) + break; + group = &ump->groups[fb->info.first_group]; + for (i = 0; i < fb->info.num_groups; i++, group++) { + group->valid = true; + if (fb->info.active) + group->active = 1; + if (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) + group->is_midi1 = true; + switch (fb->info.direction) { + case SNDRV_UMP_DIR_INPUT: + group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT); + break; + case SNDRV_UMP_DIR_OUTPUT: + group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT); + break; + case SNDRV_UMP_DIR_BIDIRECTION: + group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) | + (1 << SNDRV_RAWMIDI_STREAM_OUTPUT); + break; + } + if (!*fb->info.name) + continue; + if (*group->name) + strlcat(group->name, ", ", sizeof(group->name)); + safe_append_string(group->name, sizeof(group->name), + fb->info.name, sizeof(fb->info.name)); + } + } +} +EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs); + /* * UMP endpoint and function block handling */ @@ -602,6 +684,26 @@ static int ump_append_string(struct snd_ump_endpoint *ump, char *dest, format == UMP_STREAM_MSG_FORMAT_END); } +/* Choose the default protocol */ +static void choose_default_protocol(struct snd_ump_endpoint *ump) +{ + if (ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) + return; + if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2) + ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2; + else + ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1; +} + +/* notify the EP info/name change to sequencer */ +static void seq_notify_ep_change(struct snd_ump_endpoint *ump) +{ +#if IS_ENABLED(CONFIG_SND_SEQUENCER) + if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change) + ump->seq_ops->notify_ep_change(ump); +#endif +} + /* handle EP info stream message; update the UMP attributes */ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) @@ -623,6 +725,11 @@ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump, ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n", ump->info.version, ump->info.num_blocks, ump->info.protocol_caps); + + ump->info.protocol &= ump->info.protocol_caps; + choose_default_protocol(ump); + seq_notify_ep_change(ump); + return 1; /* finished */ } @@ -639,32 +746,51 @@ static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump, ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f; ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f; ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f; - ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%02x%02x%02x%02x\n", + ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%4phN\n", ump->info.manufacturer_id, ump->info.family_id, ump->info.model_id, - ump->info.sw_revision[0], - ump->info.sw_revision[1], - ump->info.sw_revision[2], - ump->info.sw_revision[3]); + ump->info.sw_revision); + seq_notify_ep_change(ump); return 1; /* finished */ } +/* set up the core rawmidi name from UMP EP name string */ +static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ + safe_copy_string(ump->core.name, sizeof(ump->core.name), + ump->info.name, sizeof(ump->info.name)); +} + /* handle EP name stream message; update the UMP name string */ static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) { - return ump_append_string(ump, ump->info.name, sizeof(ump->info.name), - buf->raw, 2); + int ret; + + ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name), + buf->raw, 2); + if (ret && ump->parsed) { + ump_set_rawmidi_name(ump); + ump_legacy_set_rawmidi_name(ump); + seq_notify_ep_change(ump); + } + + return ret; } /* handle EP product id stream message; update the UMP product_id string */ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) { - return ump_append_string(ump, ump->info.product_id, - sizeof(ump->info.product_id), - buf->raw, 2); + int ret; + + ret = ump_append_string(ump, ump->info.product_id, + sizeof(ump->info.product_id), + buf->raw, 2); + if (ret) + seq_notify_ep_change(ump); + return ret; } /* notify the protocol change to sequencer */ @@ -685,10 +811,17 @@ static void seq_notify_protocol(struct snd_ump_endpoint *ump) */ int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol) { + unsigned int type; + protocol &= ump->info.protocol_caps; if (protocol == ump->info.protocol) return 0; + type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK; + if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 && + type != SNDRV_UMP_EP_INFO_PROTO_MIDI2) + return 0; + ump->info.protocol = protocol; ump_dbg(ump, "New protocol = %x (caps = %x)\n", protocol, ump->info.protocol_caps); @@ -717,7 +850,10 @@ static void fill_fb_info(struct snd_ump_endpoint *ump, info->ui_hint = buf->fb_info.ui_hint; info->first_group = buf->fb_info.first_group; info->num_groups = buf->fb_info.num_groups; - info->flags = buf->fb_info.midi_10; + if (buf->fb_info.midi_10 < 2) + info->flags = buf->fb_info.midi_10; + else + info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED; info->active = buf->fb_info.active; info->midi_ci_version = buf->fb_info.midi_ci_version; info->sysex8_streams = buf->fb_info.sysex8_streams; @@ -726,6 +862,12 @@ static void fill_fb_info(struct snd_ump_endpoint *ump, info->block_id, info->direction, info->active, info->first_group, info->num_groups, info->midi_ci_version, info->sysex8_streams, info->flags); + + if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) { + info->num_groups = 1; + ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n", + info->block_id); + } } /* check whether the FB info gets updated by the current message */ @@ -779,8 +921,11 @@ static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump, if (fb) { fill_fb_info(ump, &fb->info, buf); - if (ump->parsed) + if (ump->parsed) { + snd_ump_update_group_attrs(ump); + update_legacy_names(ump); seq_notify_fb_change(ump, fb); + } } return 1; /* finished */ @@ -799,11 +944,21 @@ static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump, if (!fb) return -ENODEV; + if (ump->parsed && + (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) { + ump_dbg(ump, "Skipping static FB name update (blk#%d)\n", + fb->info.block_id); + return 0; + } + ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name), buf->raw, 3); /* notify the FB name update to sequencer, too */ - if (ret > 0 && ump->parsed) + if (ret > 0 && ump->parsed) { + snd_ump_update_group_attrs(ump); + update_legacy_names(ump); seq_notify_fb_change(ump, fb); + } return ret; } @@ -948,6 +1103,8 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump) if (err < 0) ump_dbg(ump, "Unable to get UMP EP name string\n"); + ump_set_rawmidi_name(ump); + /* Request Endpoint Product ID */ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID, UMP_STREAM_MSG_STATUS_PRODUCT_ID); @@ -960,6 +1117,9 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump) if (err < 0) ump_dbg(ump, "Unable to get UMP EP stream config\n"); + /* If no protocol is set by some reason, assume the valid one */ + choose_default_protocol(ump); + /* Query and create blocks from Function Blocks */ for (blk = 0; blk < ump->info.num_blocks; blk++) { err = create_block_from_fb_info(ump, blk); @@ -967,6 +1127,9 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump) continue; } + /* initialize group attributions */ + snd_ump_update_group_attrs(ump); + error: ump->parsed = true; ump_request_close(ump); @@ -990,6 +1153,8 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) guard(mutex)(&ump->open_mutex); if (ump->legacy_substreams[dir][group]) return -EBUSY; + if (!ump->groups[group].active) + return -ENODEV; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!ump->legacy_out_opens) { err = snd_rawmidi_kernel_open(&ump->core, 0, @@ -1069,6 +1234,7 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, struct snd_rawmidi_substream *substream; struct ump_cvt_to_ump *ctx; const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT; + unsigned int protocol; unsigned char c; int group, size = 0; @@ -1081,9 +1247,13 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, if (!substream) continue; ctx = &ump->out_cvts[group]; + protocol = ump->info.protocol; + if ((protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) && + ump->groups[group].is_midi1) + protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1; while (!ctx->ump_bytes && snd_rawmidi_transmit(substream, &c, 1) > 0) - snd_ump_convert_to_ump(ctx, group, ump->info.protocol, c); + snd_ump_convert_to_ump(ctx, group, protocol, c); if (ctx->ump_bytes && ctx->ump_bytes <= count) { size = ctx->ump_bytes; memcpy(buffer, ctx->ump, size); @@ -1140,14 +1310,39 @@ static int fill_legacy_mapping(struct snd_ump_endpoint *ump) return num; } -static void fill_substream_names(struct snd_ump_endpoint *ump, - struct snd_rawmidi *rmidi, int dir) +static void update_legacy_substreams(struct snd_ump_endpoint *ump, + struct snd_rawmidi *rmidi, int dir) { struct snd_rawmidi_substream *s; + const char *name; + int idx; + + list_for_each_entry(s, &rmidi->streams[dir].substreams, list) { + idx = ump->legacy_mapping[s->number]; + name = ump->groups[idx].name; + if (!*name) + name = ump->core.name; + scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s", + idx + 1, name, + ump->groups[idx].active ? "" : " [Inactive]"); + s->inactive = !ump->groups[idx].active; + } +} + +static void update_legacy_names(struct snd_ump_endpoint *ump) +{ + struct snd_rawmidi *rmidi = ump->legacy_rmidi; - list_for_each_entry(s, &rmidi->streams[dir].substreams, list) - snprintf(s->name, sizeof(s->name), "Group %d (%.16s)", - ump->legacy_mapping[s->number] + 1, ump->info.name); + update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); + update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); +} + +static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ + struct snd_rawmidi *rmidi = ump->legacy_rmidi; + + snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", + ump->core.name); } int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, @@ -1180,16 +1375,14 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, if (output) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ump_legacy_output_ops); - snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", - ump->info.name); rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP; rmidi->ops = &snd_ump_legacy_ops; rmidi->private_data = ump; ump->legacy_rmidi = rmidi; - if (input) - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); - if (output) - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); + ump_legacy_set_rawmidi_name(ump); + update_legacy_names(ump); + + snd_rawmidi_tie_devices(rmidi, &ump->core); ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id); return 0; diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c index de04799fdb69..0fe13d031656 100644 --- a/sound/core/ump_convert.c +++ b/sound/core/ump_convert.c @@ -287,25 +287,42 @@ static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt, return 4; } -static void fill_rpn(struct ump_cvt_to_ump_bank *cc, - union snd_ump_midi2_msg *midi2) +static void reset_rpn(struct ump_cvt_to_ump_bank *cc) { + cc->rpn_set = 0; + cc->nrpn_set = 0; + cc->cc_rpn_msb = cc->cc_rpn_lsb = 0; + cc->cc_data_msb = cc->cc_data_lsb = 0; + cc->cc_data_msb_set = cc->cc_data_lsb_set = 0; +} + +static int fill_rpn(struct ump_cvt_to_ump_bank *cc, + union snd_ump_midi2_msg *midi2, + bool flush) +{ + if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set)) + return 0; // skip + /* when not flushing, wait for complete data set */ + if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set)) + return 0; // skip + if (cc->rpn_set) { midi2->rpn.status = UMP_MSG_STATUS_RPN; midi2->rpn.bank = cc->cc_rpn_msb; midi2->rpn.index = cc->cc_rpn_lsb; - cc->rpn_set = 0; - cc->cc_rpn_msb = cc->cc_rpn_lsb = 0; - } else { + } else if (cc->nrpn_set) { midi2->rpn.status = UMP_MSG_STATUS_NRPN; midi2->rpn.bank = cc->cc_nrpn_msb; midi2->rpn.index = cc->cc_nrpn_lsb; - cc->nrpn_set = 0; - cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0; + } else { + return 0; // skip } + midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) | cc->cc_data_lsb); - cc->cc_data_msb = cc->cc_data_lsb = 0; + + reset_rpn(cc); + return 1; } /* convert to a MIDI 1.0 Channel Voice message */ @@ -318,6 +335,7 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt, struct ump_cvt_to_ump_bank *cc; union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data; unsigned char status, channel; + int ret; BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4); BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8); @@ -358,24 +376,33 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt, case UMP_MSG_STATUS_CC: switch (buf[1]) { case UMP_CC_RPN_MSB: + ret = fill_rpn(cc, midi2, true); cc->rpn_set = 1; cc->cc_rpn_msb = buf[2]; - return 0; // skip + if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) + reset_rpn(cc); + return ret; case UMP_CC_RPN_LSB: + ret = fill_rpn(cc, midi2, true); cc->rpn_set = 1; cc->cc_rpn_lsb = buf[2]; - return 0; // skip + if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f) + reset_rpn(cc); + return ret; case UMP_CC_NRPN_MSB: + ret = fill_rpn(cc, midi2, true); cc->nrpn_set = 1; cc->cc_nrpn_msb = buf[2]; - return 0; // skip + return ret; case UMP_CC_NRPN_LSB: + ret = fill_rpn(cc, midi2, true); cc->nrpn_set = 1; cc->cc_nrpn_lsb = buf[2]; - return 0; // skip + return ret; case UMP_CC_DATA: + cc->cc_data_msb_set = 1; cc->cc_data_msb = buf[2]; - return 0; // skip + return fill_rpn(cc, midi2, false); case UMP_CC_BANK_SELECT: cc->bank_set = 1; cc->cc_bank_msb = buf[2]; @@ -385,12 +412,9 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt, cc->cc_bank_lsb = buf[2]; return 0; // skip case UMP_CC_DATA_LSB: + cc->cc_data_lsb_set = 1; cc->cc_data_lsb = buf[2]; - if (cc->rpn_set || cc->nrpn_set) - fill_rpn(cc, midi2); - else - return 0; // skip - break; + return fill_rpn(cc, midi2, false); default: midi2->cc.index = buf[1]; midi2->cc.data = upscale_7_to_32bit(buf[2]); @@ -404,7 +428,6 @@ static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt, midi2->pg.bank_msb = cc->cc_bank_msb; midi2->pg.bank_lsb = cc->cc_bank_lsb; cc->bank_set = 0; - cc->cc_bank_msb = cc->cc_bank_lsb = 0; } break; case UMP_MSG_STATUS_CHANNEL_PRESSURE: diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 04a57f7be6ea..c657659b236c 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -199,6 +199,12 @@ static int follower_put(struct snd_kcontrol *kcontrol, if (err < 0) return err; for (ch = 0; ch < follower->info.count; ch++) { + if (ucontrol->value.integer.value[ch] < follower->info.min_val || + ucontrol->value.integer.value[ch] > follower->info.max_val) + return -EINVAL; + } + + for (ch = 0; ch < follower->info.count; ch++) { if (follower->vals[ch] != ucontrol->value.integer.value[ch]) { changed = 1; follower->vals[ch] = ucontrol->value.integer.value[ch]; @@ -365,6 +371,8 @@ static int master_put(struct snd_kcontrol *kcontrol, new_val = ucontrol->value.integer.value[0]; if (new_val == old_val) return 0; + if (new_val < master->info.min_val || new_val > master->info.max_val) + return -EINVAL; err = sync_followers(master, old_val, new_val); if (err < 0) |