[alsa-devel] [PATCH 22/25] ALSA: firewire-tascam: add PCM functionality

Takashi Iwai tiwai at suse.de
Mon Aug 24 23:12:39 CEST 2015


On Sat, 22 Aug 2015 11:19:38 +0200,
Takashi Sakamoto wrote:
> 
> This commit adds PCM functionality to transmit/receive PCM samples.
> 
> When one of PCM substreams are running or external clock source is
> selected, current sampling rate is used. Else, the sampling rate is
> changed as an userspace application requests.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
> ---
>  sound/firewire/tascam/Makefile     |   2 +-
>  sound/firewire/tascam/tascam-pcm.c | 287 +++++++++++++++++++++++++++++++++++++
>  sound/firewire/tascam/tascam.c     |   4 +
>  sound/firewire/tascam/tascam.h     |   2 +
>  4 files changed, 294 insertions(+), 1 deletion(-)
>  create mode 100644 sound/firewire/tascam/tascam-pcm.c
> 
> diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
> index 0a1c387..f075b9b 100644
> --- a/sound/firewire/tascam/Makefile
> +++ b/sound/firewire/tascam/Makefile
> @@ -1,3 +1,3 @@
>  snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
> -			    tascam.o
> +			    tascam-pcm.o tascam.o
>  obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
> diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
> new file mode 100644
> index 0000000..b1aa980
> --- /dev/null
> +++ b/sound/firewire/tascam/tascam-pcm.c
> @@ -0,0 +1,287 @@
> +/*
> + * tascam-pcm.c - a part of driver for TASCAM FireWire series
> + *
> + * Copyright (c) 2015 Takashi Sakamoto
> + *
> + * Licensed under the terms of the GNU General Public License, version 2.
> + */
> +
> +#include "tascam.h"
> +
> +static void set_buffer_params(struct snd_pcm_hardware *hw)
> +{
> +	hw->period_bytes_min = 4 * hw->channels_min;
> +	hw->period_bytes_max = hw->period_bytes_min * 2048;
> +	hw->buffer_bytes_max = hw->period_bytes_max * 2;
> +
> +	hw->periods_min = 2;
> +	hw->periods_max = UINT_MAX;
> +}
> +
> +static int pcm_init_hw_params(struct snd_tscm *tscm,
> +			      struct snd_pcm_substream *substream)
> +{
> +	static const struct snd_pcm_hardware hardware = {
> +		.info = SNDRV_PCM_INFO_BATCH |
> +			SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +			SNDRV_PCM_INFO_INTERLEAVED |
> +			SNDRV_PCM_INFO_JOINT_DUPLEX |
> +			SNDRV_PCM_INFO_MMAP |
> +			SNDRV_PCM_INFO_MMAP_VALID,
> +		.rates = SNDRV_PCM_RATE_44100 |
> +			 SNDRV_PCM_RATE_48000 |
> +			 SNDRV_PCM_RATE_88200 |
> +			 SNDRV_PCM_RATE_96000,
> +		.rate_min = 44100,
> +		.rate_max = 96000,
> +		.channels_min = 10,
> +		.channels_max = 18,
> +	};
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct amdtp_stream *stream;
> +	unsigned int pcm_channels;
> +
> +	runtime->hw = hardware;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
> +		stream = &tscm->tx_stream;
> +		pcm_channels = tscm->spec->pcm_capture_analog_channels;
> +		if (tscm->spec->has_adat)
> +			pcm_channels += 8;
> +		if (tscm->spec->has_spdif)
> +			pcm_channels += 2;
> +		runtime->hw.channels_min =
> +					runtime->hw.channels_max = pcm_channels;
> +	} else {
> +		runtime->hw.formats =
> +				SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
> +		stream = &tscm->rx_stream;
> +		pcm_channels = tscm->spec->pcm_playback_analog_channels;
> +		if (tscm->spec->has_adat)
> +			pcm_channels += 8;
> +		if (tscm->spec->has_spdif)
> +			pcm_channels += 2;
> +		runtime->hw.channels_min =
> +					runtime->hw.channels_max = pcm_channels;
> +	}
> +
> +	set_buffer_params(&runtime->hw);
> +
> +	return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
> +}
> +
> +static int pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_tscm *tscm = substream->private_data;
> +	enum snd_tscm_clock clock;
> +	unsigned int rate;
> +	int err;
> +
> +	err = pcm_init_hw_params(tscm, substream);
> +	if (err < 0)
> +		return err;
> +
> +	err = snd_tscm_stream_get_clock(tscm, &clock);
> +	if ((clock != SND_TSCM_CLOCK_INTERNAL) |
> +	    amdtp_stream_pcm_running(&tscm->rx_stream) |
> +	    amdtp_stream_pcm_running(&tscm->tx_stream)) {

Must be '||'.

> +		err = snd_tscm_stream_get_rate(tscm, &rate);
> +		if (err < 0)
> +			return err;
> +		substream->runtime->hw.rate_min = rate;
> +		substream->runtime->hw.rate_max = rate;
> +	}
> +
> +	snd_pcm_set_sync(substream);
> +
> +	return err;
> +}
> +
> +static int pcm_close(struct snd_pcm_substream *substream)
> +{
> +	return 0;
> +}
> +
> +static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *hw_params)
> +{
> +	struct snd_tscm *tscm = substream->private_data;
> +
> +	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
> +		mutex_lock(&tscm->mutex);
> +		tscm->substreams_counter++;
> +		mutex_unlock(&tscm->mutex);
> +	}

This looks dangerous.  For example...

> +	amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(hw_params));

... if the buffer allocation fails, this return an error.  And
snd_pcm_hw_params() calls hw_free() at the error path.  But hw_free()
decreases the counter only when the state is not OPEN.  At this
moment, however, the state is still OPEN, so the counter is unbalanced.


> +}
> +static int pcm_playback_hw_params(struct snd_pcm_substream *substream,

Put a blank line (also in many places in this file).


Takashi


More information about the Alsa-devel mailing list