[PATCH RESEND 09/11] ALSA: vsnd: Implement ALSA PCM operations
From: Oleksandr AndrushchenkoImplement ALSA driver operations including: - start/stop period interrupt emulation - manage frontend/backend shraed buffers - manage Xen bus event channel state Signed-off-by: Oleksandr Andrushchenko --- sound/drivers/xen-front.c | 175 ++ 1 file changed, 163 insertions(+), 12 deletions(-) diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c index 507c5eb343c8..7275e9cb38c0 100644 --- a/sound/drivers/xen-front.c +++ b/sound/drivers/xen-front.c @@ -151,6 +151,11 @@ struct xdrv_info { struct sdev_card_plat_data cfg_plat_data; }; +static inline void sh_buf_clear(struct sh_buf_info *buf); +static void sh_buf_free(struct sh_buf_info *buf); +static int sh_buf_alloc(struct xenbus_device *xb_dev, struct sh_buf_info *buf, + unsigned int buffer_size); + static struct sdev_pcm_stream_info *sdrv_stream_get( struct snd_pcm_substream *substream) { @@ -311,71 +316,217 @@ static void sdrv_copy_pcm_hw(struct snd_pcm_hardware *dst, static int sdrv_alsa_open(struct snd_pcm_substream *substream) { - return 0; + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct xdrv_info *xdrv_info; + unsigned long flags; + int ret; + + sdrv_copy_pcm_hw(>hw, >pcm_hw, _instance->pcm_hw); + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE); + runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED; + + xdrv_info = pcm_instance->card_info->xdrv_info; + + ret = sdrv_alsa_timer_create(substream); + + spin_lock_irqsave(_info->io_lock, flags); + sh_buf_clear(>sh_buf); + stream->evt_chnl = _info->evt_chnls[stream->unique_id]; + if (ret < 0) + stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED; + else + stream->evt_chnl->state = EVTCHNL_STATE_CONNECTED; + spin_unlock_irqrestore(_info->io_lock, flags); + return ret; } static int sdrv_alsa_close(struct snd_pcm_substream *substream) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned long flags; + + xdrv_info = pcm_instance->card_info->xdrv_info; + + sdrv_alsa_timer_stop(substream); + + spin_lock_irqsave(_info->io_lock, flags); + stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED; + spin_unlock_irqrestore(_info->io_lock, flags); return 0; } static int sdrv_alsa_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned int buffer_size; + int ret; + + buffer_size = params_buffer_bytes(params); + sh_buf_clear(>sh_buf); + xdrv_info = pcm_instance->card_info->xdrv_info; + ret = sh_buf_alloc(xdrv_info->xb_dev, + >sh_buf, buffer_size); + if (ret < 0) + goto fail; return 0; + +fail: + dev_err(_info->xb_dev->dev, + "Failed to allocate buffers for stream idx %d\n", + stream->unique_id); + return ret; } static int sdrv_alsa_hw_free(struct snd_pcm_substream *substream) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned long flags; + + xdrv_info = pcm_instance->card_info->xdrv_info; + spin_lock_irqsave(_info->io_lock, flags); + sh_buf_free(>sh_buf); + spin_unlock_irqrestore(_info->io_lock, flags); return 0; } static int sdrv_alsa_prepare(struct snd_pcm_substream *substream) { - return 0; + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + int ret = 0; + + if (!stream->is_open) + ret = sdrv_alsa_timer_prepare(substream); + return ret; } static int sdrv_alsa_trigger(struct snd_pcm_substream *substream, int cmd) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* fall through */ + case SNDRV_PCM_TRIGGER_RESUME: + return
[PATCH RESEND 09/11] ALSA: vsnd: Implement ALSA PCM operations
From: Oleksandr Andrushchenko Implement ALSA driver operations including: - start/stop period interrupt emulation - manage frontend/backend shraed buffers - manage Xen bus event channel state Signed-off-by: Oleksandr Andrushchenko --- sound/drivers/xen-front.c | 175 ++ 1 file changed, 163 insertions(+), 12 deletions(-) diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c index 507c5eb343c8..7275e9cb38c0 100644 --- a/sound/drivers/xen-front.c +++ b/sound/drivers/xen-front.c @@ -151,6 +151,11 @@ struct xdrv_info { struct sdev_card_plat_data cfg_plat_data; }; +static inline void sh_buf_clear(struct sh_buf_info *buf); +static void sh_buf_free(struct sh_buf_info *buf); +static int sh_buf_alloc(struct xenbus_device *xb_dev, struct sh_buf_info *buf, + unsigned int buffer_size); + static struct sdev_pcm_stream_info *sdrv_stream_get( struct snd_pcm_substream *substream) { @@ -311,71 +316,217 @@ static void sdrv_copy_pcm_hw(struct snd_pcm_hardware *dst, static int sdrv_alsa_open(struct snd_pcm_substream *substream) { - return 0; + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct xdrv_info *xdrv_info; + unsigned long flags; + int ret; + + sdrv_copy_pcm_hw(>hw, >pcm_hw, _instance->pcm_hw); + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE); + runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED; + + xdrv_info = pcm_instance->card_info->xdrv_info; + + ret = sdrv_alsa_timer_create(substream); + + spin_lock_irqsave(_info->io_lock, flags); + sh_buf_clear(>sh_buf); + stream->evt_chnl = _info->evt_chnls[stream->unique_id]; + if (ret < 0) + stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED; + else + stream->evt_chnl->state = EVTCHNL_STATE_CONNECTED; + spin_unlock_irqrestore(_info->io_lock, flags); + return ret; } static int sdrv_alsa_close(struct snd_pcm_substream *substream) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned long flags; + + xdrv_info = pcm_instance->card_info->xdrv_info; + + sdrv_alsa_timer_stop(substream); + + spin_lock_irqsave(_info->io_lock, flags); + stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED; + spin_unlock_irqrestore(_info->io_lock, flags); return 0; } static int sdrv_alsa_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned int buffer_size; + int ret; + + buffer_size = params_buffer_bytes(params); + sh_buf_clear(>sh_buf); + xdrv_info = pcm_instance->card_info->xdrv_info; + ret = sh_buf_alloc(xdrv_info->xb_dev, + >sh_buf, buffer_size); + if (ret < 0) + goto fail; return 0; + +fail: + dev_err(_info->xb_dev->dev, + "Failed to allocate buffers for stream idx %d\n", + stream->unique_id); + return ret; } static int sdrv_alsa_hw_free(struct snd_pcm_substream *substream) { + struct sdev_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + struct xdrv_info *xdrv_info; + unsigned long flags; + + xdrv_info = pcm_instance->card_info->xdrv_info; + spin_lock_irqsave(_info->io_lock, flags); + sh_buf_free(>sh_buf); + spin_unlock_irqrestore(_info->io_lock, flags); return 0; } static int sdrv_alsa_prepare(struct snd_pcm_substream *substream) { - return 0; + struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream); + int ret = 0; + + if (!stream->is_open) + ret = sdrv_alsa_timer_prepare(substream); + return ret; } static int sdrv_alsa_trigger(struct snd_pcm_substream *substream, int cmd) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* fall through */ + case SNDRV_PCM_TRIGGER_RESUME: + return sdrv_alsa_timer_start(substream); + + case SNDRV_PCM_TRIGGER_STOP: + /*