diff options
Diffstat (limited to 'sound/core/pcm.c')
| -rw-r--r-- | sound/core/pcm.c | 189 | 
1 files changed, 136 insertions, 53 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6b4b1287b31..43932e8dce6 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -21,8 +21,10 @@  #include <linux/init.h>  #include <linux/slab.h> +#include <linux/module.h>  #include <linux/time.h>  #include <linux/mutex.h> +#include <linux/device.h>  #include <sound/core.h>  #include <sound/minors.h>  #include <sound/pcm.h> @@ -47,6 +49,8 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)  	struct snd_pcm *pcm;  	list_for_each_entry(pcm, &snd_pcm_devices, list) { +		if (pcm->internal) +			continue;  		if (pcm->card == card && pcm->device == device)  			return pcm;  	} @@ -58,6 +62,8 @@ static int snd_pcm_next(struct snd_card *card, int device)  	struct snd_pcm *pcm;  	list_for_each_entry(pcm, &snd_pcm_devices, list) { +		if (pcm->internal) +			continue;  		if (pcm->card == card && pcm->device > device)  			return pcm->device;  		else if (pcm->card->number > card->number) @@ -207,13 +213,15 @@ static char *snd_pcm_format_names[] = {  	FORMAT(G723_24_1B),  	FORMAT(G723_40),  	FORMAT(G723_40_1B), +	FORMAT(DSD_U8), +	FORMAT(DSD_U16_LE),  };  const char *snd_pcm_format_name(snd_pcm_format_t format)  { -	if (format >= ARRAY_SIZE(snd_pcm_format_names)) +	if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))  		return "Unknown"; -	return snd_pcm_format_names[format]; +	return snd_pcm_format_names[(__force unsigned int)format];  }  EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -269,12 +277,12 @@ static const char *snd_pcm_stream_name(int stream)  static const char *snd_pcm_access_name(snd_pcm_access_t access)  { -	return snd_pcm_access_names[access]; +	return snd_pcm_access_names[(__force int)access];  }  static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)  { -	return snd_pcm_subformat_names[subformat]; +	return snd_pcm_subformat_names[(__force int)subformat];  }  static const char *snd_pcm_tstamp_mode_name(int mode) @@ -284,10 +292,10 @@ static const char *snd_pcm_tstamp_mode_name(int mode)  static const char *snd_pcm_state_name(snd_pcm_state_t state)  { -	return snd_pcm_state_names[state]; +	return snd_pcm_state_names[(__force int)state];  } -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS)  #include <linux/soundcard.h>  static const char *snd_pcm_oss_format_name(int format) @@ -330,7 +338,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,  	info = kmalloc(sizeof(*info), GFP_KERNEL);  	if (! info) { -		printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); +		pcm_dbg(substream->pcm, +			"snd_pcm_proc_info_read: cannot malloc\n");  		return;  	} @@ -390,7 +399,7 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,  	snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);	  	snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);	  	snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);	 -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS)  	if (substream->oss.oss) {  		snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));  		snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);	 @@ -635,7 +644,7 @@ static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substrea   * calling this, i.e. zero must be given to the argument of   * snd_pcm_new().   * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure.   */  int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  { @@ -643,16 +652,16 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  	struct snd_pcm_str *pstr = &pcm->streams[stream];  	struct snd_pcm_substream *substream, *prev; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS)  	mutex_init(&pstr->oss.setup_mutex);  #endif  	pstr->stream = stream;  	pstr->pcm = pcm;  	pstr->substream_count = substream_count; -	if (substream_count > 0) { +	if (substream_count > 0 && !pcm->internal) {  		err = snd_pcm_stream_proc_init(pstr);  		if (err < 0) { -			snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); +			pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");  			return err;  		}  	} @@ -660,7 +669,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  	for (idx = 0, prev = NULL; idx < substream_count; idx++) {  		substream = kzalloc(sizeof(*substream), GFP_KERNEL);  		if (substream == NULL) { -			snd_printk(KERN_ERR "Cannot allocate PCM substream\n"); +			pcm_err(pcm, "Cannot allocate PCM substream\n");  			return -ENOMEM;  		}  		substream->pcm = pcm; @@ -673,15 +682,19 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  			pstr->substream = substream;  		else  			prev->next = substream; -		err = snd_pcm_substream_proc_init(substream); -		if (err < 0) { -			snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); -			if (prev == NULL) -				pstr->substream = NULL; -			else -				prev->next = NULL; -			kfree(substream); -			return err; + +		if (!pcm->internal) { +			err = snd_pcm_substream_proc_init(substream); +			if (err < 0) { +				pcm_err(pcm, +					"Error in snd_pcm_stream_proc_init\n"); +				if (prev == NULL) +					pstr->substream = NULL; +				else +					prev->next = NULL; +				kfree(substream); +				return err; +			}  		}  		substream->group = &substream->self_group;  		spin_lock_init(&substream->self_group.lock); @@ -695,25 +708,9 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)  EXPORT_SYMBOL(snd_pcm_new_stream); -/** - * snd_pcm_new - create a new PCM instance - * @card: the card instance - * @id: the id string - * @device: the device index (zero based) - * @playback_count: the number of substreams for playback - * @capture_count: the number of substreams for capture - * @rpcm: the pointer to store the new pcm instance - * - * Creates a new PCM instance. - * - * The pcm operators have to be set afterwards to the new instance - * via snd_pcm_set_ops(). - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_new(struct snd_card *card, const char *id, int device, -		int playback_count, int capture_count, -	        struct snd_pcm ** rpcm) +static int _snd_pcm_new(struct snd_card *card, const char *id, int device, +		int playback_count, int capture_count, bool internal, +		struct snd_pcm **rpcm)  {  	struct snd_pcm *pcm;  	int err; @@ -729,11 +726,12 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device,  		*rpcm = NULL;  	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);  	if (pcm == NULL) { -		snd_printk(KERN_ERR "Cannot allocate PCM\n"); +		dev_err(card->dev, "Cannot allocate PCM\n");  		return -ENOMEM;  	}  	pcm->card = card;  	pcm->device = device; +	pcm->internal = internal;  	if (id)  		strlcpy(pcm->id, id, sizeof(pcm->id));  	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { @@ -755,12 +753,63 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device,  	return 0;  } +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_new(struct snd_card *card, const char *id, int device, +		int playback_count, int capture_count, struct snd_pcm **rpcm) +{ +	return _snd_pcm_new(card, id, device, playback_count, capture_count, +			false, rpcm); +}  EXPORT_SYMBOL(snd_pcm_new); +/** + * snd_pcm_new_internal - create a new internal PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based - shared with normal PCMs) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new internal PCM instance with no userspace device or procfs + * entries. This is used by ASoC Back End PCMs in order to create a PCM that + * will only be used internally by kernel drivers. i.e. it cannot be opened + * by userspace. It provides existing ASoC components drivers with a substream + * and access to any private data. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, +	int playback_count, int capture_count, +	struct snd_pcm **rpcm) +{ +	return _snd_pcm_new(card, id, device, playback_count, capture_count, +			true, rpcm); +} +EXPORT_SYMBOL(snd_pcm_new_internal); +  static void snd_pcm_free_stream(struct snd_pcm_str * pstr)  {  	struct snd_pcm_substream *substream, *substream_next; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS)  	struct snd_pcm_oss_setup *setup, *setupn;  #endif  	substream = pstr->substream; @@ -772,7 +821,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)  		substream = substream_next;  	}  	snd_pcm_stream_proc_done(pstr); -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS)  	for (setup = pstr->oss.setup_list; setup; setup = setupn) {  		setupn = setup->next;  		kfree(setup->task_name); @@ -940,8 +989,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)  		       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));  	kfree(runtime->hw_constraints.rules);  #ifdef CONFIG_SND_PCM_XRUN_DEBUG -	if (runtime->hwptr_log) -		kfree(runtime->hwptr_log); +	kfree(runtime->hwptr_log);  #endif  	kfree(runtime);  	substream->runtime = NULL; @@ -970,8 +1018,20 @@ static ssize_t show_pcm_class(struct device *dev,          return snprintf(buf, PAGE_SIZE, "%s\n", str);  } -static struct device_attribute pcm_attrs = -	__ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static struct attribute *pcm_dev_attrs[] = { +	&dev_attr_pcm_class.attr, +	NULL +}; + +static struct attribute_group pcm_dev_attr_group = { +	.attrs	= pcm_dev_attrs, +}; + +static const struct attribute_group *pcm_dev_attr_groups[] = { +	&pcm_dev_attr_group, +	NULL +};  static int snd_pcm_dev_register(struct snd_device *device)  { @@ -993,7 +1053,7 @@ static int snd_pcm_dev_register(struct snd_device *device)  	}  	for (cidx = 0; cidx < 2; cidx++) {  		int devtype = -1; -		if (pcm->streams[cidx].substream == NULL) +		if (pcm->streams[cidx].substream == NULL || pcm->internal)  			continue;  		switch (cidx) {  		case SNDRV_PCM_STREAM_PLAYBACK: @@ -1021,8 +1081,18 @@ static int snd_pcm_dev_register(struct snd_device *device)  			mutex_unlock(®ister_mutex);  			return err;  		} -		snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, -					  &pcm_attrs); + +		dev = snd_get_device(devtype, pcm->card, pcm->device); +		if (dev) { +			err = sysfs_create_groups(&dev->kobj, +						  pcm_dev_attr_groups); +			if (err < 0) +				dev_warn(dev, +					 "pcm %d:%d: cannot create sysfs groups\n", +					 pcm->card->number, pcm->device); +			put_device(dev); +		} +  		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)  			snd_pcm_timer_init(substream);  	} @@ -1045,11 +1115,19 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)  	if (list_empty(&pcm->list))  		goto unlock; +	mutex_lock(&pcm->open_mutex); +	wake_up(&pcm->open_wait);  	list_del_init(&pcm->list);  	for (cidx = 0; cidx < 2; cidx++) -		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) -			if (substream->runtime) +		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { +			snd_pcm_stream_lock_irq(substream); +			if (substream->runtime) {  				substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; +				wake_up(&substream->runtime->sleep); +				wake_up(&substream->runtime->tsleep); +			} +			snd_pcm_stream_unlock_irq(substream); +		}  	list_for_each_entry(notify, &snd_pcm_notify_list, list) {  		notify->n_disconnect(pcm);  	} @@ -1064,7 +1142,12 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)  			break;  		}  		snd_unregister_device(devtype, pcm->card, pcm->device); +		if (pcm->streams[cidx].chmap_kctl) { +			snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); +			pcm->streams[cidx].chmap_kctl = NULL; +		}  	} +	mutex_unlock(&pcm->open_mutex);   unlock:  	mutex_unlock(®ister_mutex);  	return 0;  | 
