[alsa-devel] [PATCH v2] Add M2Tech hiFace USB-SPDIF driver
Takashi Iwai
tiwai at suse.de
Fri Feb 22 13:53:06 CET 2013
At Wed, 13 Feb 2013 18:11:45 +0100,
Antonio Ospite wrote:
> new file mode 100644
> index 0000000..d0c3c58
> --- /dev/null
> +++ b/sound/usb/hiface/pcm.c
> @@ -0,0 +1,638 @@
....
> +struct pcm_runtime {
> + struct hiface_chip *chip;
> + struct snd_pcm *instance;
> +
> + struct pcm_substream playback;
> + bool panic; /* if set driver won't do anymore pcm on device */
Once when rt->panic is set, how can you recover?
I see no code resetting rt->panic.
> +/* The hardware wants word-swapped 32-bit values */
> +static void memcpy_swahw32(u8 *dest, u8 *src, unsigned int n)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < n / 4; i++)
> + ((u32 *)dest)[i] = swahw32(((u32 *)src)[i]);
> +}
> +
> +/* call with substream locked */
> +/* returns true if a period elapsed */
> +static bool hiface_pcm_playback(struct pcm_substream *sub,
> + struct pcm_urb *urb)
> +{
> + struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
> + u8 *source;
> + unsigned int pcm_buffer_size;
> +
> + WARN_ON(alsa_rt->format != SNDRV_PCM_FORMAT_S32_LE);
Can't we use simply SNDRV_PCM_FORMAT_S32_BE without the byte swapping
above?
> +static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
> +{
> + struct pcm_urb *out_urb = usb_urb->context;
> + struct pcm_runtime *rt = out_urb->chip->pcm;
> + struct pcm_substream *sub;
> + bool do_period_elapsed = false;
> + unsigned long flags;
> + int ret;
> +
> + pr_debug("%s: called.\n", __func__);
> +
> + if (rt->panic || rt->stream_state == STREAM_STOPPING)
> + return;
> +
> + if (unlikely(usb_urb->status == -ENOENT || /* unlinked */
> + usb_urb->status == -ENODEV || /* device removed */
> + usb_urb->status == -ECONNRESET || /* unlinked */
> + usb_urb->status == -ESHUTDOWN)) { /* device disabled */
> + goto out_fail;
> + }
> +
> + if (rt->stream_state == STREAM_STARTING) {
> + rt->stream_wait_cond = true;
> + wake_up(&rt->stream_wait_queue);
> + }
> +
> + /* now send our playback data (if a free out urb was found) */
> + sub = &rt->playback;
> + spin_lock_irqsave(&sub->lock, flags);
> + if (sub->active)
> + do_period_elapsed = hiface_pcm_playback(sub, out_urb);
> + else
> + memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
> +
> + spin_unlock_irqrestore(&sub->lock, flags);
> +
> + if (do_period_elapsed)
> + snd_pcm_period_elapsed(sub->instance);
This looks like a small open race. sub->instance might be set to NULL
after the unlock above?
> +
> + ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
> + if (ret < 0)
> + goto out_fail;
Maybe better to check the state again before resubmission, e.g. when
XRUN is detected in the pointer callback.
> +
> + return;
> +
> +out_fail:
> + rt->panic = true;
> +}
> +
> +static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
> +{
> + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
> + struct pcm_substream *sub = NULL;
> + struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
> + int ret;
> +
> + pr_debug("%s: called.\n", __func__);
> +
> + if (rt->panic)
> + return -EPIPE;
> +
> + mutex_lock(&rt->stream_mutex);
> + alsa_rt->hw = pcm_hw;
> +
> + if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + sub = &rt->playback;
> +
> + if (!sub) {
> + mutex_unlock(&rt->stream_mutex);
> + pr_err("Invalid stream type\n");
> + return -EINVAL;
> + }
> +
> + if (rt->extra_freq) {
> + alsa_rt->hw.rates |= SNDRV_PCM_RATE_KNOT;
> + alsa_rt->hw.rate_max = 384000;
> +
> + /* explicit constraints needed as we added SNDRV_PCM_RATE_KNOT */
> + ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0,
> + SNDRV_PCM_HW_PARAM_RATE,
> + &constraints_extra_rates);
> + if (ret < 0) {
> + mutex_unlock(&rt->stream_mutex);
> + return ret;
> + }
> + }
> +
> + sub->instance = alsa_sub;
> + sub->active = false;
> + mutex_unlock(&rt->stream_mutex);
> + return 0;
> +}
> +
> +static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub)
> +{
> + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
> + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
> + unsigned long flags;
> +
> + if (rt->panic)
> + return 0;
> +
> + pr_debug("%s: called.\n", __func__);
> +
> + mutex_lock(&rt->stream_mutex);
> + if (sub) {
> + /* deactivate substream */
> + spin_lock_irqsave(&sub->lock, flags);
> + sub->instance = NULL;
> + sub->active = false;
> + spin_unlock_irqrestore(&sub->lock, flags);
> +
> + /* all substreams closed? if so, stop streaming */
> + if (!rt->playback.instance)
> + hiface_pcm_stream_stop(rt);
Can this condition be ever false...?
> +static int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
> +{
> + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
> + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
> + unsigned long flags;
For trigger callback, you need no irqsave but simply use
spin_lock_irq().
> +void hiface_pcm_abort(struct hiface_chip *chip)
> +{
> + struct pcm_runtime *rt = chip->pcm;
> +
> + if (rt) {
> + rt->panic = true;
> +
> + if (rt->playback.instance) {
> + snd_pcm_stream_lock_irq(rt->playback.instance);
> + snd_pcm_stop(rt->playback.instance,
> + SNDRV_PCM_STATE_XRUN);
> + snd_pcm_stream_unlock_irq(rt->playback.instance);
> + }
Hmm... this is called after snd_card_disconnect(), thus it will reset
the PCM stream state from DISCONNECTED to XRUN. It doesn't sound
right.
> +int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
> +{
....
> + snd_pcm_lib_preallocate_pages_for_all(pcm,
> + SNDRV_DMA_TYPE_CONTINUOUS,
> + snd_dma_continuous_data(GFP_KERNEL),
> + PCM_BUFFER_SIZE, PCM_BUFFER_SIZE);
Any reason not to use vmalloc buffer?
thanks,
Takashi
More information about the Alsa-devel
mailing list