diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 43 | ||||
-rw-r--r-- | sound/core/hrtimer.c | 3 | ||||
-rw-r--r-- | sound/core/memalloc.c | 2 | ||||
-rw-r--r-- | sound/core/memory.c | 41 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 2 | ||||
-rw-r--r-- | sound/core/rawmidi.c | 3 | ||||
-rw-r--r-- | sound/core/seq/Kconfig | 4 | ||||
-rw-r--r-- | sound/core/seq/oss/seq_oss_device.h | 5 | ||||
-rw-r--r-- | sound/core/seq/oss/seq_oss_init.c | 8 | ||||
-rw-r--r-- | sound/core/seq/oss/seq_oss_synth.c | 82 | ||||
-rw-r--r-- | sound/core/seq/seq_clientmgr.c | 45 | ||||
-rw-r--r-- | sound/core/seq/seq_system.c | 34 | ||||
-rw-r--r-- | sound/core/seq/seq_system.h | 35 | ||||
-rw-r--r-- | sound/core/seq/seq_ump_client.c | 42 | ||||
-rw-r--r-- | sound/core/ump.c | 114 |
15 files changed, 297 insertions, 166 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 86ed2fbee0c8..840bb9cfe789 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1025,7 +1025,7 @@ static u64 snd_compr_seqno_next(struct snd_compr_stream *stream) static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask) { struct snd_compr_task_runtime *task; - int retval; + int retval, fd_i, fd_o; if (stream->runtime->total_tasks >= stream->runtime->fragments) return -EBUSY; @@ -1039,19 +1039,27 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ retval = stream->ops->task_create(stream, task); if (retval < 0) goto cleanup; - utask->input_fd = dma_buf_fd(task->input, O_WRONLY|O_CLOEXEC); - if (utask->input_fd < 0) { - retval = utask->input_fd; + /* 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; } - utask->output_fd = dma_buf_fd(task->output, O_RDONLY|O_CLOEXEC); - if (utask->output_fd < 0) { - retval = utask->output_fd; + 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 */ - dma_buf_get(utask->input_fd); - dma_buf_get(utask->output_fd); + 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; @@ -1069,7 +1077,7 @@ static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long return -EPERM; task = memdup_user((void __user *)arg, sizeof(*task)); if (IS_ERR(task)) - return PTR_ERR(no_free_ptr(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))) @@ -1130,7 +1138,7 @@ static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned return -EPERM; task = memdup_user((void __user *)arg, sizeof(*task)); if (IS_ERR(task)) - return PTR_ERR(no_free_ptr(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))) @@ -1174,18 +1182,18 @@ typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream, 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; + struct snd_compr_task_runtime *task, *temp; __u64 seqno; int retval; if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) return -EPERM; - retval = get_user(seqno, (__u64 __user *)arg); - if (retval < 0) - return retval; + retval = copy_from_user(&seqno, (__u64 __user *)arg, sizeof(seqno)); + if (retval) + return -EFAULT; retval = 0; if (seqno == 0) { - list_for_each_entry_reverse(task, &stream->runtime->tasks, list) + list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list) fcn(stream, task); } else { task = snd_compr_find_task(stream, seqno); @@ -1221,7 +1229,7 @@ static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned return -EPERM; status = memdup_user((void __user *)arg, sizeof(*status)); if (IS_ERR(status)) - return PTR_ERR(no_free_ptr(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))) @@ -1247,6 +1255,7 @@ void snd_compr_task_finished(struct snd_compr_stream *stream, } 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) diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 147c1fea4708..e9c60dce59fb 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -66,9 +66,8 @@ static int snd_hrtimer_open(struct snd_timer *t) stime = kzalloc(sizeof(*stime), GFP_KERNEL); if (!stime) return -ENOMEM; - hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); stime->timer = t; - stime->hrt.function = snd_hrtimer_callback; + hrtimer_setup(&stime->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL); t->private_data = stime; return 0; } diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 13b71069ae18..b3853583d2ae 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -505,7 +505,7 @@ static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size) if (!p) return NULL; dmab->addr = dma_map_single(dmab->dev.dev, p, size, DMA_BIDIRECTIONAL); - if (dmab->addr == DMA_MAPPING_ERROR) { + if (dma_mapping_error(dmab->dev.dev, dmab->addr)) { do_free_pages(dmab->area, size, true); return NULL; } 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/pcm_native.c b/sound/core/pcm_native.c index 8a3384342e8d..6c2b6a62d9d2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3245,7 +3245,7 @@ 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(bufs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 348ce1b7725e..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; } 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/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index 98dd20b42976..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; @@ -157,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 3930e2f9082f..cb66ec42a3f8 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -678,12 +678,18 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, dest_port->time_real); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { - if (snd_seq_ev_is_ump(event)) { + if (snd_seq_ev_is_ump(event)) { + if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { result = snd_seq_deliver_from_ump(client, dest, dest_port, event, atomic, hop); goto __skip; - } else if (snd_seq_client_is_ump(dest)) { + } else if (dest->type == USER_CLIENT && + !snd_seq_client_is_ump(dest)) { + result = 0; // drop the event + goto __skip; + } + } else if (snd_seq_client_is_ump(dest)) { + if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { result = snd_seq_deliver_to_ump(client, dest, dest_port, event, atomic, hop); goto __skip; @@ -1275,10 +1281,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]) @@ -1290,6 +1302,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; } @@ -1413,6 +1429,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; } @@ -1469,7 +1488,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 */ } @@ -2223,6 +2242,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 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_ump_client.c b/sound/core/seq/seq_ump_client.c index e956f17f3792..1255351b59ce 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -272,8 +272,6 @@ static void update_port_infos(struct seq_ump_client *client) new); if (err < 0) continue; - /* notify to system port */ - snd_seq_system_client_ev_port_change(client->seq_client, i); } } @@ -390,6 +388,33 @@ static void handle_group_notify(struct work_struct *work) 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) @@ -399,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, }; diff --git a/sound/core/ump.c b/sound/core/ump.c index fe4d39ae1159..8d8681a42ca5 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -37,6 +37,7 @@ 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, @@ -48,11 +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, @@ -565,16 +597,10 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) } 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 */ + if (*group->name) strlcat(group->name, ", ", sizeof(group->name)); - strlcat(group->name, fb->info.name, - sizeof(group->name)); - } + safe_append_string(group->name, sizeof(group->name), + fb->info.name, sizeof(fb->info.name)); } } } @@ -669,6 +695,15 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump) 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) @@ -693,6 +728,7 @@ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump, ump->info.protocol &= ump->info.protocol_caps; choose_default_protocol(ump); + seq_notify_ep_change(ump); return 1; /* finished */ } @@ -715,24 +751,46 @@ static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump, ump->info.family_id, ump->info.model_id, 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 */ @@ -1045,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); @@ -1244,14 +1304,14 @@ static int fill_legacy_mapping(struct snd_ump_endpoint *ump) num = 0; for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) - if ((group_maps & (1U << i)) && ump->groups[i].valid) + if (group_maps & (1U << i)) ump->legacy_mapping[num++] = i; 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; @@ -1261,10 +1321,11 @@ static void fill_substream_names(struct snd_ump_endpoint *ump, idx = ump->legacy_mapping[s->number]; name = ump->groups[idx].name; if (!*name) - name = ump->info.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; } } @@ -1272,8 +1333,16 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) { struct snd_rawmidi *rmidi = ump->legacy_rmidi; - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); + 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, @@ -1306,14 +1375,15 @@ 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; + 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; } |