[alsa-devel] [PATCH 22/25] ALSA: firewire-tascam: add PCM functionality
Takashi Sakamoto
o-takashi at sakamocchi.jp
Tue Aug 25 13:56:00 CEST 2015
On Aug 25 2015 06:12, Takashi Iwai wrote:
> 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.
Indeed. I missed this case. I think moving the increment of substream
counter aftre keeping buffer.
And Fireworks/BeBoB/OXFW/Dice drivers have the same issue.
>> +}
>> +static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
>
> Put a blank line (also in many places in this file).
Thanks
Takashi Sakamoto
More information about the Alsa-devel
mailing list