diff options
Diffstat (limited to 'sound/core/pcm.c')
| -rw-r--r-- | sound/core/pcm.c | 474 |
1 files changed, 219 insertions, 255 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 89c7485519cb..283aac441fa0 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1,22 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Digital Audio (PCM) abstract layer * Copyright (c) by Jaroslav Kysela <perex@perex.cz> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/init.h> @@ -25,9 +10,11 @@ #include <linux/time.h> #include <linux/mutex.h> #include <linux/device.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/pcm.h> +#include <sound/timer.h> #include <sound/control.h> #include <sound/info.h> @@ -104,9 +91,8 @@ static int snd_pcm_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)arg)) return -EFAULT; - mutex_lock(®ister_mutex); - device = snd_pcm_next(card, device); - mutex_unlock(®ister_mutex); + scoped_guard(mutex, ®ister_mutex) + device = snd_pcm_next(card, device); if (put_user(device, (int __user *)arg)) return -EFAULT; return 0; @@ -119,7 +105,6 @@ static int snd_pcm_control_ioctl(struct snd_card *card, struct snd_pcm *pcm; struct snd_pcm_str *pstr; struct snd_pcm_substream *substream; - int err; info = (struct snd_pcm_info __user *)arg; if (get_user(device, &info->device)) @@ -128,35 +113,26 @@ static int snd_pcm_control_ioctl(struct snd_card *card, return -EFAULT; if (stream < 0 || stream > 1) return -EINVAL; + stream = array_index_nospec(stream, 2); if (get_user(subdevice, &info->subdevice)) return -EFAULT; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); pcm = snd_pcm_get(card, device); - if (pcm == NULL) { - err = -ENXIO; - goto _error; - } + if (pcm == NULL) + return -ENXIO; pstr = &pcm->streams[stream]; - if (pstr->substream_count == 0) { - err = -ENOENT; - goto _error; - } - if (subdevice >= pstr->substream_count) { - err = -ENXIO; - goto _error; - } + if (pstr->substream_count == 0) + return -ENOENT; + if (subdevice >= pstr->substream_count) + return -ENXIO; for (substream = pstr->substream; substream; substream = substream->next) if (substream->number == (int)subdevice) break; - if (substream == NULL) { - err = -ENXIO; - goto _error; - } - err = snd_pcm_info_user(substream, info); - _error: - mutex_unlock(®ister_mutex); - return err; + if (substream == NULL) + return -ENXIO; + guard(mutex)(&pcm->open_mutex); + return snd_pcm_info_user(substream, info); } case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: { @@ -173,7 +149,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v -static char *snd_pcm_format_names[] = { +static const char * const snd_pcm_format_names[] = { FORMAT(S8), FORMAT(U8), FORMAT(S16_LE), @@ -221,17 +197,25 @@ static char *snd_pcm_format_names[] = { FORMAT(DSD_U32_LE), FORMAT(DSD_U16_BE), FORMAT(DSD_U32_BE), + FORMAT(S20_LE), + FORMAT(S20_BE), + FORMAT(U20_LE), + FORMAT(U20_BE), }; /** * snd_pcm_format_name - Return a name string for the given PCM format * @format: PCM format + * + * Return: the format name string */ const char *snd_pcm_format_name(snd_pcm_format_t format) { - if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) + unsigned int format_num = (__force unsigned int)format; + + if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num]) return "Unknown"; - return snd_pcm_format_names[(__force unsigned int)format]; + return snd_pcm_format_names[format_num]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -247,12 +231,12 @@ EXPORT_SYMBOL_GPL(snd_pcm_format_name); #define START(v) [SNDRV_PCM_START_##v] = #v #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v -static char *snd_pcm_stream_names[] = { +static const char * const snd_pcm_stream_names[] = { STREAM(PLAYBACK), STREAM(CAPTURE), }; -static char *snd_pcm_state_names[] = { +static const char * const snd_pcm_state_names[] = { STATE(OPEN), STATE(SETUP), STATE(PREPARED), @@ -261,9 +245,10 @@ static char *snd_pcm_state_names[] = { STATE(DRAINING), STATE(PAUSED), STATE(SUSPENDED), + STATE(DISCONNECTED), }; -static char *snd_pcm_access_names[] = { +static const char * const snd_pcm_access_names[] = { ACCESS(MMAP_INTERLEAVED), ACCESS(MMAP_NONINTERLEAVED), ACCESS(MMAP_COMPLEX), @@ -271,11 +256,14 @@ static char *snd_pcm_access_names[] = { ACCESS(RW_NONINTERLEAVED), }; -static char *snd_pcm_subformat_names[] = { +static const char * const snd_pcm_subformat_names[] = { SUBFORMAT(STD), + SUBFORMAT(MSBITS_MAX), + SUBFORMAT(MSBITS_20), + SUBFORMAT(MSBITS_24), }; -static char *snd_pcm_tstamp_mode_names[] = { +static const char * const snd_pcm_tstamp_mode_names[] = { TSTAMP(NONE), TSTAMP(ENABLE), }; @@ -340,7 +328,7 @@ static const char *snd_pcm_oss_format_name(int format) static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, struct snd_info_buffer *buffer) { - struct snd_pcm_info *info; + struct snd_pcm_info *info __free(kfree) = NULL; int err; if (! substream) @@ -353,7 +341,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, err = snd_pcm_info(substream, info); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - kfree(info); return; } snd_iprintf(buffer, "card: %d\n", info->card); @@ -367,7 +354,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); - kfree(info); } static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, @@ -389,15 +375,15 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + if (runtime->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - goto unlock; + return; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); @@ -416,8 +402,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif - unlock: - mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, @@ -426,15 +410,15 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + if (runtime->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - goto unlock; + return; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); @@ -444,8 +428,6 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); - unlock: - mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, @@ -453,35 +435,36 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, { struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - struct snd_pcm_status status; + struct snd_pcm_status64 status; int err; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } memset(&status, 0, sizeof(status)); - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - goto unlock; + return; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); - snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", - status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); - snd_iprintf(buffer, "tstamp : %ld.%09ld\n", - status.tstamp.tv_sec, status.tstamp.tv_nsec); + snd_iprintf(buffer, "trigger_time: %lld.%09lld\n", + status.trigger_tstamp_sec, status.trigger_tstamp_nsec); + snd_iprintf(buffer, "tstamp : %lld.%09lld\n", + status.tstamp_sec, status.tstamp_nsec); snd_iprintf(buffer, "delay : %ld\n", status.delay); snd_iprintf(buffer, "avail : %ld\n", status.avail); snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); snd_iprintf(buffer, "-----\n"); snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); - unlock: - mutex_unlock(&substream->pcm->open_mutex); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + snd_iprintf(buffer, "xrun_counter: %d\n", substream->xrun_counter); +#endif } #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -489,13 +472,8 @@ static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime; - snd_pcm_stream_lock_irq(substream); - runtime = substream->runtime; - if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(substream); + snd_pcm_stop_xrun(substream); } static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, @@ -523,54 +501,49 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) sprintf(name, "pcm%i%c", pcm->device, pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); - if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) + entry = snd_info_create_card_entry(pcm->card, name, + pcm->card->proc_root); + if (!entry) return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } + entry->mode = S_IFDIR | 0555; pstr->proc_root = entry; - - if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { + entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); + if (entry) snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - pstr->proc_info_entry = entry; - #ifdef CONFIG_SND_PCM_XRUN_DEBUG - if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", - pstr->proc_root)) != NULL) { - entry->c.text.read = snd_pcm_xrun_debug_read; + entry = snd_info_create_card_entry(pcm->card, "xrun_debug", + pstr->proc_root); + if (entry) { + snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read); entry->c.text.write = snd_pcm_xrun_debug_write; - entry->mode |= S_IWUSR; - entry->private_data = pstr; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } + entry->mode |= 0200; } - pstr->proc_xrun_debug_entry = entry; #endif return 0; } static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - snd_info_free_entry(pstr->proc_xrun_debug_entry); - pstr->proc_xrun_debug_entry = NULL; -#endif - snd_info_free_entry(pstr->proc_info_entry); - pstr->proc_info_entry = NULL; snd_info_free_entry(pstr->proc_root); pstr->proc_root = NULL; return 0; } +static struct snd_info_entry * +create_substream_info_entry(struct snd_pcm_substream *substream, + const char *name, + void (*read)(struct snd_info_entry *, + struct snd_info_buffer *)) +{ + struct snd_info_entry *entry; + + entry = snd_info_create_card_entry(substream->pcm->card, name, + substream->proc_root); + if (entry) + snd_info_set_text_ops(entry, substream, read); + return entry; +} + static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { struct snd_info_entry *entry; @@ -580,101 +553,64 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) card = substream->pcm->card; sprintf(name, "sub%i", substream->number); - if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) + entry = snd_info_create_card_entry(card, name, + substream->pstr->proc_root); + if (!entry) return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } + entry->mode = S_IFDIR | 0555; substream->proc_root = entry; - if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, - snd_pcm_substream_proc_info_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - substream->proc_info_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, - snd_pcm_substream_proc_hw_params_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - substream->proc_hw_params_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, - snd_pcm_substream_proc_sw_params_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - substream->proc_sw_params_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, - snd_pcm_substream_proc_status_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - substream->proc_status_entry = entry; + create_substream_info_entry(substream, "info", + snd_pcm_substream_proc_info_read); + create_substream_info_entry(substream, "hw_params", + snd_pcm_substream_proc_hw_params_read); + create_substream_info_entry(substream, "sw_params", + snd_pcm_substream_proc_sw_params_read); + create_substream_info_entry(substream, "status", + snd_pcm_substream_proc_status_read); #ifdef CONFIG_SND_PCM_XRUN_DEBUG - entry = snd_info_create_card_entry(card, "xrun_injection", - substream->proc_root); + entry = create_substream_info_entry(substream, "xrun_injection", NULL); if (entry) { - entry->private_data = substream; - entry->c.text.read = NULL; entry->c.text.write = snd_pcm_xrun_injection_write; - entry->mode = S_IFREG | S_IWUSR; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } + entry->mode = S_IFREG | 0200; } - substream->proc_xrun_injection_entry = entry; #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ return 0; } -static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) -{ - snd_info_free_entry(substream->proc_info_entry); - substream->proc_info_entry = NULL; - snd_info_free_entry(substream->proc_hw_params_entry); - substream->proc_hw_params_entry = NULL; - snd_info_free_entry(substream->proc_sw_params_entry); - substream->proc_sw_params_entry = NULL; - snd_info_free_entry(substream->proc_status_entry); - substream->proc_status_entry = NULL; -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - snd_info_free_entry(substream->proc_xrun_injection_entry); - substream->proc_xrun_injection_entry = NULL; -#endif - snd_info_free_entry(substream->proc_root); - substream->proc_root = NULL; - return 0; -} #else /* !CONFIG_SND_VERBOSE_PROCFS */ static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } -static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; } #endif /* CONFIG_SND_VERBOSE_PROCFS */ static const struct attribute_group *pcm_dev_attr_groups[]; +/* + * PM callbacks: we need to deal only with suspend here, as the resume is + * triggered either from user-space or the driver's resume callback + */ +static int do_pcm_suspend(struct device *dev) +{ + struct snd_pcm_str *pstr = dev_get_drvdata(dev); + + if (!pstr->pcm->no_device_suspend) + snd_pcm_suspend_all(pstr->pcm); + return 0; +} + +static const struct dev_pm_ops pcm_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) +}; + +/* device type for PCM -- basically only for passing PM callbacks */ +static const struct device_type pcm_dev_type = { + .name = "pcm", + .pm = &pcm_dev_pm_ops, +}; + /** * snd_pcm_new_stream - create a new PCM stream * @pcm: the pcm instance @@ -703,10 +639,14 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) if (!substream_count) return 0; - snd_device_initialize(&pstr->dev, pcm->card); - pstr->dev.groups = pcm_dev_attr_groups; - dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, + err = snd_device_alloc(&pstr->dev, pcm->card); + if (err < 0) + return err; + dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); + pstr->dev->groups = pcm_dev_attr_groups; + pstr->dev->type = &pcm_dev_type; + dev_set_drvdata(pstr->dev, pstr); if (!pcm->internal) { err = snd_pcm_stream_proc_init(pstr); @@ -745,9 +685,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } } substream->group = &substream->self_group; - spin_lock_init(&substream->self_group.lock); - mutex_init(&substream->self_group.mutex); - INIT_LIST_HEAD(&substream->self_group.substreams); + snd_pcm_group_init(&substream->self_group); list_add_tail(&substream->link_list, &substream->self_group.substreams); atomic_set(&substream->mmap_count, 0); prev = substream; @@ -762,11 +700,14 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, { struct snd_pcm *pcm; int err; - static struct snd_device_ops ops = { + static const struct snd_device_ops ops = { .dev_free = snd_pcm_dev_free, .dev_register = snd_pcm_dev_register, .dev_disconnect = snd_pcm_dev_disconnect, }; + static const struct snd_device_ops internal_ops = { + .dev_free = snd_pcm_dev_free, + }; if (snd_BUG_ON(!card)) return -ENXIO; @@ -782,22 +723,29 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, init_waitqueue_head(&pcm->open_wait); INIT_LIST_HEAD(&pcm->list); if (id) - strlcpy(pcm->id, id, sizeof(pcm->id)); - if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { - snd_pcm_free(pcm); - return err; - } - if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { - snd_pcm_free(pcm); - return err; - } - if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { - snd_pcm_free(pcm); - return err; - } + strscpy(pcm->id, id, sizeof(pcm->id)); + + err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, + playback_count); + if (err < 0) + goto free_pcm; + + err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); + if (err < 0) + goto free_pcm; + + err = snd_device_new(card, SNDRV_DEV_PCM, pcm, + internal ? &internal_ops : &ops); + if (err < 0) + goto free_pcm; + if (rpcm) *rpcm = pcm; return 0; + +free_pcm: + snd_pcm_free(pcm); + return err; } /** @@ -856,7 +804,9 @@ EXPORT_SYMBOL(snd_pcm_new_internal); static void free_chmap(struct snd_pcm_str *pstr) { if (pstr->chmap_kctl) { - snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + struct snd_card *card = pstr->pcm->card; + + snd_ctl_remove(card, pstr->chmap_kctl); pstr->chmap_kctl = NULL; } } @@ -867,15 +817,17 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) #if IS_ENABLED(CONFIG_SND_PCM_OSS) struct snd_pcm_oss_setup *setup, *setupn; #endif + + /* free all proc files under the stream */ + snd_pcm_stream_proc_done(pstr); + substream = pstr->substream; while (substream) { substream_next = substream->next; snd_pcm_timer_done(substream); - snd_pcm_substream_proc_done(substream); kfree(substream); substream = substream_next; } - snd_pcm_stream_proc_done(pstr); #if IS_ENABLED(CONFIG_SND_PCM_OSS) for (setup = pstr->oss.setup_list; setup; setup = setupn) { setupn = setup->next; @@ -885,7 +837,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) #endif free_chmap(pstr); if (pstr->substream_count) - put_device(&pstr->dev); + put_device(pstr->dev); } #if IS_ENABLED(CONFIG_SND_PCM_OSS) @@ -988,27 +940,29 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, return -ENOMEM; size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); - runtime->status = snd_malloc_pages(size, GFP_KERNEL); + runtime->status = alloc_pages_exact(size, GFP_KERNEL); if (runtime->status == NULL) { kfree(runtime); return -ENOMEM; } - memset((void*)runtime->status, 0, size); + memset(runtime->status, 0, size); size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); - runtime->control = snd_malloc_pages(size, GFP_KERNEL); + runtime->control = alloc_pages_exact(size, GFP_KERNEL); if (runtime->control == NULL) { - snd_free_pages((void*)runtime->status, + free_pages_exact(runtime->status, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); kfree(runtime); return -ENOMEM; } - memset((void*)runtime->control, 0, size); + memset(runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); init_waitqueue_head(&runtime->tsleep); - runtime->status->state = SNDRV_PCM_STATE_OPEN; + __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); + mutex_init(&runtime->buffer_mutex); + atomic_set(&runtime->buffer_accessing, 0); substream->runtime = runtime; substream->private_data = pcm->private_data; @@ -1017,6 +971,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; } @@ -1029,22 +986,30 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) runtime = substream->runtime; if (runtime->private_free != NULL) runtime->private_free(runtime); - snd_free_pages((void*)runtime->status, + free_pages_exact(runtime->status, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); - snd_free_pages((void*)runtime->control, + free_pages_exact(runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); + /* Avoid concurrent access to runtime via PCM timer interface */ + if (substream->timer) { + scoped_guard(spinlock_irq, &substream->timer->lock) + substream->runtime = NULL; + } else { + substream->runtime = NULL; + } + mutex_destroy(&runtime->buffer_mutex); + snd_fasync_free(runtime->fasync); kfree(runtime); - substream->runtime = NULL; put_pid(substream->pid); substream->pid = NULL; substream->pstr->substream_opened--; } -static ssize_t show_pcm_class(struct device *dev, +static ssize_t pcm_class_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); + struct snd_pcm_str *pstr = dev_get_drvdata(dev); struct snd_pcm *pcm = pstr->pcm; const char *str; static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { @@ -1058,10 +1023,10 @@ static ssize_t show_pcm_class(struct device *dev, str = "none"; else str = strs[pcm->dev_class]; - return snprintf(buf, PAGE_SIZE, "%s\n", str); + return sysfs_emit(buf, "%s\n", str); } -static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static DEVICE_ATTR_RO(pcm_class); static struct attribute *pcm_dev_attrs[] = { &dev_attr_pcm_class.attr, NULL @@ -1085,13 +1050,11 @@ static int snd_pcm_dev_register(struct snd_device *device) if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; - if (pcm->internal) - return 0; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); err = snd_pcm_add(pcm); if (err) - goto unlock; + return err; for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; if (pcm->streams[cidx].substream == NULL) @@ -1107,10 +1070,10 @@ static int snd_pcm_dev_register(struct snd_device *device) /* register pcm */ err = snd_register_device(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, - &pcm->streams[cidx].dev); + pcm->streams[cidx].dev); if (err < 0) { list_del_init(&pcm->list); - goto unlock; + return err; } for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) @@ -1118,9 +1081,6 @@ static int snd_pcm_dev_register(struct snd_device *device) } pcm_call_notify(pcm, n_register); - - unlock: - mutex_unlock(®ister_mutex); return err; } @@ -1130,31 +1090,34 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) struct snd_pcm_substream *substream; int cidx; - mutex_lock(®ister_mutex); - mutex_lock(&pcm->open_mutex); + guard(mutex)(®ister_mutex); + guard(mutex)(&pcm->open_mutex); wake_up(&pcm->open_wait); list_del_init(&pcm->list); - for (cidx = 0; cidx < 2; cidx++) { - for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { - snd_pcm_stream_lock_irq(substream); - if (substream->runtime) { - substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; - wake_up(&substream->runtime->sleep); - wake_up(&substream->runtime->tsleep); - } - snd_pcm_stream_unlock_irq(substream); + + for_each_pcm_substream(pcm, cidx, substream) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime) { + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + /* to be sure, set the state unconditionally */ + __snd_pcm_set_state(substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + wake_up(&substream->runtime->sleep); + wake_up(&substream->runtime->tsleep); } + snd_pcm_stream_unlock_irq(substream); } - if (!pcm->internal) { - pcm_call_notify(pcm, n_disconnect); - } + + for_each_pcm_substream(pcm, cidx, substream) + snd_pcm_sync_stop(substream, false); + + pcm_call_notify(pcm, n_disconnect); for (cidx = 0; cidx < 2; cidx++) { - if (!pcm->internal) - snd_unregister_device(&pcm->streams[cidx].dev); + if (pcm->streams[cidx].dev) + snd_unregister_device(pcm->streams[cidx].dev); free_chmap(&pcm->streams[cidx]); } - mutex_unlock(&pcm->open_mutex); - mutex_unlock(®ister_mutex); return 0; } @@ -1167,6 +1130,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) * This adds the given notifier to the global list so that the callback is * called for each registered PCM devices. This exists only for PCM OSS * emulation, so far. + * + * Return: zero if successful, or a negative error code */ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { @@ -1177,7 +1142,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) !notify->n_unregister || !notify->n_disconnect)) return -EINVAL; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); if (nfree) { list_del(¬ify->list); list_for_each_entry(pcm, &snd_pcm_devices, list) @@ -1187,7 +1152,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) list_for_each_entry(pcm, &snd_pcm_devices, list) notify->n_register(pcm); } - mutex_unlock(®ister_mutex); return 0; } EXPORT_SYMBOL(snd_pcm_notify); @@ -1203,7 +1167,7 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry, { struct snd_pcm *pcm; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_for_each_entry(pcm, &snd_pcm_devices, list) { snd_iprintf(buffer, "%02i-%02i: %s : %s", pcm->card->number, pcm->device, pcm->id, pcm->name); @@ -1215,7 +1179,6 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); snd_iprintf(buffer, "\n"); } - mutex_unlock(®ister_mutex); } static struct snd_info_entry *snd_pcm_proc_entry; @@ -1224,7 +1187,8 @@ static void snd_pcm_proc_init(void) { struct snd_info_entry *entry; - if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { + entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); + if (entry) { snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); |
