Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

From: Oleksandr Andrushchenko
Date: Tue Apr 17 2018 - 08:26:26 EST


On 04/17/2018 02:32 PM, Oleksandr Andrushchenko wrote:
On 04/16/2018 05:09 PM, Juergen Gross wrote:
On 16/04/18 08:24, Oleksandr Andrushchenko wrote:
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>

Implement essential initialization of the sound driver:
ÂÂ - introduce required data structures
ÂÂ - handle driver registration
ÂÂ - handle sound card registration
ÂÂ - register sound driver on backend connection
ÂÂ - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the
Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
 - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS
ÂÂÂ notifications from the backend when stream position advances
ÂÂÂ during playback/capture. The event carries a value of how
ÂÂÂ many octets were played/captured at the time of the event.
 - implement explicit stream parameter negotiation between
ÂÂÂ backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request
ÂÂÂ to read/update configuration space for the parameter given:
ÂÂÂ request passes desired parameter interval and the response to
ÂÂÂ this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
---
 sound/xen/Makefile | 3 +-
 sound/xen/xen_snd_front.c | 193 ++++++++-
 sound/xen/xen_snd_front.h | 28 ++
 sound/xen/xen_snd_front_alsa.c | 830 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_alsa.h | 23 ++
 sound/xen/xen_snd_front_evtchnl.c | 6 +-
 6 files changed, 1080 insertions(+), 3 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_alsa.c
 create mode 100644 sound/xen/xen_snd_front_alsa.h

diff --git a/sound/xen/Makefile b/sound/xen/Makefile
index f028bc30af5d..1e6470ecc2f2 100644
--- a/sound/xen/Makefile
+++ b/sound/xen/Makefile
@@ -3,6 +3,7 @@
 snd_xen_front-objs := xen_snd_front.o \
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_cfg.o \
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_evtchnl.o \
-ÂÂÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_shbuf.o
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_shbuf.o \
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_alsa.o
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c
index 0569c6c596a3..1fef253ea21a 100644
--- a/sound/xen/xen_snd_front.c
+++ b/sound/xen/xen_snd_front.c
@@ -19,10 +19,201 @@
 #include <xen/interface/io/sndif.h>
  #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
 #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation)
+{
+ÂÂÂ struct xensnd_req *req;
+
+ÂÂÂ req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ evtchnl->u.req.ring.req_prod_pvt);
+ÂÂÂ req->operation = operation;
+ÂÂÂ req->id = evtchnl->evt_next_id++;
+ÂÂÂ evtchnl->evt_id = req->id;
+ÂÂÂ return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+ÂÂÂ if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+ÂÂÂÂÂÂÂ return -EIO;
+
+ÂÂÂ reinit_completion(&evtchnl->u.req.completion);
+ÂÂÂ xen_snd_front_evtchnl_flush(evtchnl);
+ÂÂÂ return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+ÂÂÂ if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+ÂÂÂÂÂÂÂÂÂÂÂ msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+ÂÂÂÂÂÂÂ return -ETIMEDOUT;
+
+ÂÂÂ return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xensnd_query_hw_param *hw_param_req,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xensnd_query_hw_param *hw_param_resp)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+ÂÂÂ req->op.hw_param = *hw_param_req;
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xen_snd_front_shbuf *sh_buf,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u8 format, unsigned int channels,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned int rate, u32 buffer_sz,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u32 period_sz)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+ÂÂÂ req->op.open.pcm_format = format;
+ÂÂÂ req->op.open.pcm_channels = channels;
+ÂÂÂ req->op.open.pcm_rate = rate;
+ÂÂÂ req->op.open.buffer_sz = buffer_sz;
+ÂÂÂ req->op.open.period_sz = period_sz;
+ÂÂÂ req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long pos, unsigned long count)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+ÂÂÂ req->op.rw.length = count;
+ÂÂÂ req->op.rw.offset = pos;
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long pos, unsigned long count)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+ÂÂÂ req->op.rw.length = count;
+ÂÂÂ req->op.rw.offset = pos;
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int type)
+{
+ÂÂÂ struct xen_snd_front_info *front_info = evtchnl->front_info;
+ÂÂÂ struct xensnd_req *req;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&evtchnl->u.req.req_io_lock);
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+ÂÂÂ req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+ÂÂÂ req->op.trigger.type = type;
+
+ÂÂÂ ret = be_stream_do_io(evtchnl);
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ if (ret == 0)
+ÂÂÂÂÂÂÂ ret = be_stream_wait_io(evtchnl);
+
+ÂÂÂ mutex_unlock(&evtchnl->u.req.req_io_lock);
+ÂÂÂ return ret;
+}
  static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+ÂÂÂ xen_snd_front_alsa_fini(front_info);
ÂÂÂÂÂ xen_snd_front_evtchnl_free_all(front_info);
 }
 @@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
  static int sndback_connect(struct xen_snd_front_info *front_info)
 {
-ÂÂÂ return 0;
+ÂÂÂ return xen_snd_front_alsa_init(front_info);
 }
  static void sndback_disconnect(struct xen_snd_front_info *front_info)
diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h
index 9c2ffbb4e4b8..7adbdb4d2019 100644
--- a/sound/xen/xen_snd_front.h
+++ b/sound/xen/xen_snd_front.h
@@ -13,17 +13,45 @@
  #include "xen_snd_front_cfg.h"
 +struct card_info;
+struct xen_snd_front_evtchnl;
 struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
  struct xen_snd_front_info {
ÂÂÂÂÂ struct xenbus_device *xb_dev;
 + struct card_info *card_info;
+
ÂÂÂÂÂ /* serializer for backend IO: request/response */
ÂÂÂÂÂ spinlock_t io_lock;
+
ÂÂÂÂÂ int num_evt_pairs;
ÂÂÂÂÂ struct xen_snd_front_evtchnl_pair *evt_pairs;
 Â struct xen_front_cfg_card cfg;
 };
 +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xensnd_query_hw_param *hw_param_req,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xen_snd_front_shbuf *sh_buf,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u8 format, unsigned int channels,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned int rate, u32 buffer_sz,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int type);
+
 #endif /* __XEN_SND_FRONT_H */
diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c
new file mode 100644
index 000000000000..f524b172750e
--- /dev/null
+++ b/sound/xen/xen_snd_front_alsa.c
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {
Not sure how this is generally handled in the sound drivers, but when
reviewing the code using those structures I repeatedly tried to find
their definitions in the sound headers instead of here. Same applies to
the alsa_* names.

I'd prefer names which don't poison the name space.
I'll try to do something about naming
One question still remains wrt alsa_* names: if this is for structures
I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then
those already have sndif in their name which clearly says these are
Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all
static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {

-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {

-struct card_info {
+struct xen_snd_front_card_info {

Does the above work for you?
+ÂÂÂ struct xen_snd_front_info *front_info;
+ÂÂÂ struct xen_snd_front_evtchnl_pair *evt_pair;
+ÂÂÂ struct xen_snd_front_shbuf sh_buf;
+ÂÂÂ int index;
+
+ÂÂÂ bool is_open;
+ÂÂÂ struct snd_pcm_hardware pcm_hw;
+
+ÂÂÂ /* number of processed frames as reported by the backend */
+ÂÂÂ snd_pcm_uframes_t be_cur_frame;
+ÂÂÂ /* current HW pointer to be reported via .period callback */
+ÂÂÂ atomic_t hw_ptr;
+ÂÂÂ /* modulo of the number of processed frames - for period detection */
+ÂÂÂ u32 out_frames;
+};
+
+struct pcm_instance_info {
+ÂÂÂ struct card_info *card_info;
+ÂÂÂ struct snd_pcm *pcm;
+ÂÂÂ struct snd_pcm_hardware pcm_hw;
+ÂÂÂ int num_pcm_streams_pb;
+ÂÂÂ struct pcm_stream_info *streams_pb;
+ÂÂÂ int num_pcm_streams_cap;
+ÂÂÂ struct pcm_stream_info *streams_cap;
+};
+
+struct card_info {
+ÂÂÂ struct xen_snd_front_info *front_info;
+ÂÂÂ struct snd_card *card;
+ÂÂÂ struct snd_pcm_hardware pcm_hw;
+ÂÂÂ int num_pcm_instances;
+ÂÂÂ struct pcm_instance_info *pcm_instances;
+};
+
+struct alsa_sndif_sample_format {
+ÂÂÂ u8 sndif;
+ÂÂÂ snd_pcm_format_t alsa;
+};
+
+struct alsa_sndif_hw_param {
+ÂÂÂ u8 sndif;
+ÂÂÂ snd_pcm_hw_param_t alsa;
+};
+
+static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U8,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U8
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S8,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S8
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U16_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U16_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U16_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U16_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S16_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S16_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S16_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S16_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U24_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U24_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U24_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U24_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S24_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S24_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S24_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S24_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U32_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U32_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_U32_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_U32_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S32_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S32_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_S32_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_S32_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_A_LAW,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_A_LAW
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_MU_LAW,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_MU_LAW
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_F32_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_F32_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_F64_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_F64_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_MPEG,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_MPEG
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .sndif = XENSND_PCM_FORMAT_GSM,
+ÂÂÂÂÂÂÂ .alsa = SNDRV_PCM_FORMAT_GSM
+ÂÂÂ },
+};
+
+static int to_sndif_format(snd_pcm_format_t format)
+{
+ÂÂÂ int i;
+
+ÂÂÂ for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+ÂÂÂÂÂÂÂ if (ALSA_SNDIF_FORMATS[i].alsa == format)
+ÂÂÂÂÂÂÂÂÂÂÂ return ALSA_SNDIF_FORMATS[i].sndif;
+
+ÂÂÂ return -EINVAL;
+}
+
+static u64 to_sndif_formats_mask(u64 alsa_formats)
+{
+ÂÂÂ u64 mask;
+ÂÂÂ int i;
+
+ÂÂÂ mask = 0;
+ÂÂÂ for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+ÂÂÂÂÂÂÂ if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats)
+ÂÂÂÂÂÂÂÂÂÂÂ mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+
+ÂÂÂ return mask;
+}
+
+static u64 to_alsa_formats_mask(u64 sndif_formats)
+{
+ÂÂÂ u64 mask;
+ÂÂÂ int i;
+
+ÂÂÂ mask = 0;
+ÂÂÂ for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+ÂÂÂÂÂÂÂ if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+ÂÂÂÂÂÂÂÂÂÂÂ mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa;
+
+ÂÂÂ return mask;
+}
+
+static void stream_clear(struct pcm_stream_info *stream)
+{
+ÂÂÂ stream->is_open = false;
+ÂÂÂ stream->be_cur_frame = 0;
+ÂÂÂ stream->out_frames = 0;
+ÂÂÂ atomic_set(&stream->hw_ptr, 0);
+ÂÂÂ xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
+ÂÂÂ xen_snd_front_shbuf_clear(&stream->sh_buf);
+}
+
+static void stream_free(struct pcm_stream_info *stream)
+{
+ÂÂÂ xen_snd_front_shbuf_free(&stream->sh_buf);
+ÂÂÂ stream_clear(stream);
+}
+
+static struct pcm_stream_info *stream_get(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_instance_info *pcm_instance =
+ÂÂÂÂÂÂÂÂÂÂÂ snd_pcm_substream_chip(substream);
+ÂÂÂ struct pcm_stream_info *stream;
+
+ÂÂÂ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ÂÂÂÂÂÂÂ stream = &pcm_instance->streams_pb[substream->number];
+ÂÂÂ else
+ÂÂÂÂÂÂÂ stream = &pcm_instance->streams_cap[substream->number];
+
+ÂÂÂ return stream;
+}
+
+static int alsa_hw_rule(struct snd_pcm_hw_params *params,
+ÂÂÂÂÂÂÂÂÂÂÂ struct snd_pcm_hw_rule *rule)
+{
+ÂÂÂ struct pcm_stream_info *stream = rule->private;
+ÂÂÂ struct device *dev = &stream->front_info->xb_dev->dev;
+ÂÂÂ struct snd_mask *formats =
+ÂÂÂÂÂÂÂÂÂÂÂ hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ÂÂÂ struct snd_interval *rates =
+ÂÂÂÂÂÂÂÂÂÂÂ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ÂÂÂ struct snd_interval *channels =
+ÂÂÂÂÂÂÂÂÂÂÂ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ÂÂÂ struct snd_interval *period =
+ÂÂÂÂÂÂÂÂÂÂÂ hw_param_interval(params,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ÂÂÂ struct snd_interval *buffer =
+ÂÂÂÂÂÂÂÂÂÂÂ hw_param_interval(params,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+ÂÂÂ struct xensnd_query_hw_param req;
+ÂÂÂ struct xensnd_query_hw_param resp;
+ÂÂÂ struct snd_interval interval;
+ÂÂÂ struct snd_mask mask;
+ÂÂÂ u64 sndif_formats;
+ÂÂÂ int changed, ret;
+
+ÂÂÂ /* collect all the values we need for the query */
+
+ÂÂÂ req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ (u64)(formats->bits[1]) << 32);
+
+ÂÂÂ req.rates.min = rates->min;
+ÂÂÂ req.rates.max = rates->max;
+
+ÂÂÂ req.channels.min = channels->min;
+ÂÂÂ req.channels.max = channels->max;
+
+ÂÂÂ req.buffer.min = buffer->min;
+ÂÂÂ req.buffer.max = buffer->max;
+
+ÂÂÂ req.period.min = period->min;
+ÂÂÂ req.period.max = period->max;
+
+ÂÂÂ ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &req, &resp);
+ÂÂÂ if (ret < 0) {
+ÂÂÂÂÂÂÂ /* check if this is due to backend communication error */
+ÂÂÂÂÂÂÂ if (ret == -EIO || ret == -ETIMEDOUT)
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(dev, "Failed to query ALSA HW parameters\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ /* refine HW parameters after the query */
+ changed = 0;
+
+ÂÂÂ sndif_formats = to_alsa_formats_mask(resp.formats);
+ÂÂÂ snd_mask_none(&mask);
+ÂÂÂ mask.bits[0] = (u32)sndif_formats;
+ÂÂÂ mask.bits[1] = (u32)(sndif_formats >> 32);
+ÂÂÂ ret = snd_mask_refine(formats, &mask);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ changed |= ret;
+
+ÂÂÂ interval.openmin = 0;
+ÂÂÂ interval.openmax = 0;
+ÂÂÂ interval.integer = 1;
+
+ÂÂÂ interval.min = resp.rates.min;
+ÂÂÂ interval.max = resp.rates.max;
+ÂÂÂ ret = snd_interval_refine(rates, &interval);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ changed |= ret;
+
+ÂÂÂ interval.min = resp.channels.min;
+ÂÂÂ interval.max = resp.channels.max;
+ÂÂÂ ret = snd_interval_refine(channels, &interval);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ changed |= ret;
+
+ÂÂÂ interval.min = resp.buffer.min;
+ÂÂÂ interval.max = resp.buffer.max;
+ÂÂÂ ret = snd_interval_refine(buffer, &interval);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ changed |= ret;
+
+ÂÂÂ interval.min = resp.period.min;
+ÂÂÂ interval.max = resp.period.max;
+ÂÂÂ ret = snd_interval_refine(period, &interval);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ changed |= ret;
+
+ÂÂÂ return changed;
+}
+
+static int alsa_open(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_instance_info *pcm_instance =
+ÂÂÂÂÂÂÂÂÂÂÂ snd_pcm_substream_chip(substream);
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ struct snd_pcm_runtime *runtime = substream->runtime;
+ÂÂÂ struct xen_snd_front_info *front_info =
+ÂÂÂÂÂÂÂÂÂÂÂ pcm_instance->card_info->front_info;
+ÂÂÂ struct device *dev = &front_info->xb_dev->dev;
+ÂÂÂ unsigned long flags;
+ÂÂÂ int ret;
+
+ÂÂÂ /*
+ÂÂÂÂ * return our HW properties: override defaults with those configured
+ÂÂÂÂ * via XenStore
+ÂÂÂÂ */
+ÂÂÂ runtime->hw = stream->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;
+
+ÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
+
+ÂÂÂ stream->evt_pair = &front_info->evt_pairs[stream->index];
+
+ÂÂÂ stream->front_info = front_info;
+
+ xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
+
+ÂÂÂ stream->evt_pair->evt.u.evt.substream = substream;
+
+ÂÂÂ stream_clear(stream);
+
+ÂÂÂ spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+ÂÂÂ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ alsa_hw_rule, stream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ alsa_hw_rule, stream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_RATE, -1);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ alsa_hw_rule, stream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ alsa_hw_rule, stream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ alsa_hw_rule, stream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ return 0;
+}
+
+static int alsa_close(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ unsigned long flags;
+
+ÂÂÂ spin_lock_irqsave(&stream->front_info->io_lock, flags);
+
+ xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
+
+ spin_unlock_irqrestore(&stream->front_info->io_lock, flags);
+ÂÂÂ return 0;
+}
+
+static int alsa_hw_params(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ struct snd_pcm_hw_params *params)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ int ret;
+
+ÂÂÂ /*
+ÂÂÂÂ * this callback may be called multiple times,
+ÂÂÂÂ * so free the previously allocated shared buffer if any
+ÂÂÂÂ */
+ÂÂÂ stream_free(stream);
+
+ÂÂÂ ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &stream->sh_buf,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ params_buffer_bytes(params));
+ÂÂÂ if (ret < 0) {
+ÂÂÂÂÂÂÂ stream_free(stream);
+ dev_err(&stream->front_info->xb_dev->dev,
+ÂÂÂÂÂÂÂÂÂÂÂ "Failed to allocate buffers for stream with index %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂ stream->index);
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ return 0;
+}
+
+static int alsa_hw_free(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ int ret;
+
+ÂÂÂ ret = xen_snd_front_stream_close(&stream->evt_pair->req);
+ÂÂÂ stream_free(stream);
+ÂÂÂ return ret;
+}
+
+static int alsa_prepare(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+
+ÂÂÂ if (!stream->is_open) {
+ÂÂÂÂÂÂÂ struct snd_pcm_runtime *runtime = substream->runtime;
+ÂÂÂÂÂÂÂ u8 sndif_format;
+ÂÂÂÂÂÂÂ int ret;
+
+ÂÂÂÂÂÂÂ sndif_format = to_sndif_format(runtime->format);
+ÂÂÂÂÂÂÂ if (sndif_format < 0) {
+ dev_err(&stream->front_info->xb_dev->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Unsupported sample format: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ runtime->format);
+ÂÂÂÂÂÂÂÂÂÂÂ return sndif_format;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &stream->sh_buf,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ sndif_format,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ runtime->channels,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ runtime->rate,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream));
+ÂÂÂÂÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂÂÂÂÂ stream->is_open = true;
+ÂÂÂ }
+
+ÂÂÂ return 0;
+}
+
+static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ int type;
+
+ÂÂÂ switch (cmd) {
+ÂÂÂ case SNDRV_PCM_TRIGGER_START:
+ÂÂÂÂÂÂÂ type = XENSND_OP_TRIGGER_START;
+ÂÂÂÂÂÂÂ break;
+
+ÂÂÂ case SNDRV_PCM_TRIGGER_RESUME:
+ÂÂÂÂÂÂÂ type = XENSND_OP_TRIGGER_RESUME;
+ÂÂÂÂÂÂÂ break;
+
+ÂÂÂ case SNDRV_PCM_TRIGGER_STOP:
+ÂÂÂÂÂÂÂ type = XENSND_OP_TRIGGER_STOP;
+ÂÂÂÂÂÂÂ break;
+
+ÂÂÂ case SNDRV_PCM_TRIGGER_SUSPEND:
+ÂÂÂÂÂÂÂ type = XENSND_OP_TRIGGER_PAUSE;
+ÂÂÂÂÂÂÂ break;
+
+ÂÂÂ default:
+ÂÂÂÂÂÂÂ return -EINVAL;
+ÂÂÂ }
+
+ÂÂÂ return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
+}
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u64 pos_bytes)
+{
+ÂÂÂ struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
+
+ÂÂÂ cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
+
+ÂÂÂ delta = cur_frame - stream->be_cur_frame;
+ÂÂÂ stream->be_cur_frame = cur_frame;
+
+ÂÂÂ new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+ÂÂÂ new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
+ÂÂÂ atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
+
+ÂÂÂ stream->out_frames += delta;
+ÂÂÂ if (stream->out_frames > substream->runtime->period_size) {
+ÂÂÂÂÂÂÂ stream->out_frames %= substream->runtime->period_size;
+ÂÂÂÂÂÂÂ snd_pcm_period_elapsed(substream);
+ÂÂÂ }
+}
+
+static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+
+ÂÂÂ return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+}
+
+static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int channel, unsigned long pos, void __user *src,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long count)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+
+ÂÂÂ if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
+ÂÂÂÂÂÂÂ return -EFAULT;
+
+ÂÂÂ return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int channel, unsigned long pos, void *src,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long count)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+
+ÂÂÂ if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ memcpy(stream->sh_buf.buffer + pos, src, count);
+
+ÂÂÂ return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int channel, unsigned long pos, void __user *dst,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long count)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ int ret;
+
+ÂÂÂ if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
+ÂÂÂÂÂÂÂ -EFAULT : 0;
+}
+
+static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int channel, unsigned long pos, void *dst,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long count)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+ÂÂÂ int ret;
+
+ÂÂÂ if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ memcpy(dst, stream->sh_buf.buffer + pos, count);
+
+ÂÂÂ return 0;
+}
+
+static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int channel, unsigned long pos,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long count)
+{
+ÂÂÂ struct pcm_stream_info *stream = stream_get(substream);
+
+ÂÂÂ if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ memset(stream->sh_buf.buffer + pos, 0, count);
+
+ÂÂÂ return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+/*
+ * FIXME: The mmaped data transfer is asynchronous and there is no
+ * ack signal from user-space when it is done. This is the
+ * reason it is not implemented in the PV driver as we do need
+ * to know when the buffer can be transferred to the backend.
+ */
+
+static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
+ÂÂÂ .open = alsa_open,
+ÂÂÂ .close = alsa_close,
+ÂÂÂ .ioctl = snd_pcm_lib_ioctl,
+ÂÂÂ .hw_params = alsa_hw_params,
+ÂÂÂ .hw_free = alsa_hw_free,
+ÂÂÂ .prepare = alsa_prepare,
+ÂÂÂ .trigger = alsa_trigger,
+ÂÂÂ .pointer = alsa_pointer,
+ÂÂÂ .copy_user = alsa_pb_copy_user,
+ÂÂÂ .copy_kernel = alsa_pb_copy_kernel,
+ÂÂÂ .fill_silence = alsa_pb_fill_silence,
+};
+
+static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
+ÂÂÂ .open = alsa_open,
+ÂÂÂ .close = alsa_close,
+ÂÂÂ .ioctl = snd_pcm_lib_ioctl,
+ÂÂÂ .hw_params = alsa_hw_params,
+ÂÂÂ .hw_free = alsa_hw_free,
+ÂÂÂ .prepare = alsa_prepare,
+ÂÂÂ .trigger = alsa_trigger,
+ÂÂÂ .pointer = alsa_pointer,
+ÂÂÂ .copy_user = alsa_cap_copy_user,
+ÂÂÂ .copy_kernel = alsa_cap_copy_kernel,
+};
+
+static int new_pcm_instance(struct card_info *card_info,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct xen_front_cfg_pcm_instance *instance_cfg,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct pcm_instance_info *pcm_instance_info)
+{
+ÂÂÂ struct snd_pcm *pcm;
+ÂÂÂ int ret, i;
+
+ÂÂÂ dev_dbg(&card_info->front_info->xb_dev->dev,
+ÂÂÂÂÂÂÂ "New PCM device \"%s\" with id %d playback %d capture %d",
+ÂÂÂÂÂÂÂ instance_cfg->name,
+ÂÂÂÂÂÂÂ instance_cfg->device_id,
+ÂÂÂÂÂÂÂ instance_cfg->num_streams_pb,
+ÂÂÂÂÂÂÂ instance_cfg->num_streams_cap);
+
+ÂÂÂ pcm_instance_info->card_info = card_info;
+
+ÂÂÂ pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
+
+ÂÂÂ if (instance_cfg->num_streams_pb) {
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_pb =
+ devm_kcalloc(&card_info->card->card_dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_pb,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ sizeof(struct pcm_stream_info),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂÂÂÂÂ if (!pcm_instance_info->streams_pb)
+ÂÂÂÂÂÂÂÂÂÂÂ return -ENOMEM;
+ÂÂÂ }
+
+ÂÂÂ if (instance_cfg->num_streams_cap) {
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_cap =
+ devm_kcalloc(&card_info->card->card_dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_cap,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ sizeof(struct pcm_stream_info),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂÂÂÂÂ if (!pcm_instance_info->streams_cap)
+ÂÂÂÂÂÂÂÂÂÂÂ return -ENOMEM;
+ÂÂÂ }
+
+ÂÂÂ pcm_instance_info->num_pcm_streams_pb =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_pb;
+ÂÂÂ pcm_instance_info->num_pcm_streams_cap =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_cap;
+
+ÂÂÂ for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_pb[i].pcm_hw =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->streams_pb[i].pcm_hw;
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_pb[i].index =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->streams_pb[i].index;
+ÂÂÂ }
+
+ÂÂÂ for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_cap[i].pcm_hw =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->streams_cap[i].pcm_hw;
+ÂÂÂÂÂÂÂ pcm_instance_info->streams_cap[i].index =
+ÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->streams_cap[i].index;
+ÂÂÂ }
+
+ÂÂÂ ret = snd_pcm_new(card_info->card, instance_cfg->name,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->device_id,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_pb,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ instance_cfg->num_streams_cap,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂ &pcm);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ pcm->private_data = pcm_instance_info;
+ÂÂÂ pcm->info_flags = 0;
+ÂÂÂ /* we want to handle all PCM operations in non-atomic context */
+ÂÂÂ pcm->nonatomic = true;
+ÂÂÂ strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+
+ÂÂÂ if (instance_cfg->num_streams_pb)
+ÂÂÂÂÂÂÂ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &snd_drv_alsa_playback_ops);
+
+ÂÂÂ if (instance_cfg->num_streams_cap)
+ÂÂÂÂÂÂÂ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &snd_drv_alsa_capture_ops);
+
+ÂÂÂ pcm_instance_info->pcm = pcm;
+ÂÂÂ return 0;
+}
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
+{
+ÂÂÂ struct device *dev = &front_info->xb_dev->dev;
+ÂÂÂ struct xen_front_cfg_card *cfg = &front_info->cfg;
+ÂÂÂ struct card_info *card_info;
+ÂÂÂ struct snd_card *card;
+ÂÂÂ int ret, i;
+
+ÂÂÂ dev_dbg(dev, "Creating virtual sound card\n");
+
+ÂÂÂ ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ sizeof(struct card_info), &card);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ card_info = card->private_data;
+ÂÂÂ card_info->front_info = front_info;
+ÂÂÂ front_info->card_info = card_info;
+ÂÂÂ card_info->card = card;
+ÂÂÂ card_info->pcm_instances =
+ÂÂÂÂÂÂÂÂÂÂÂ devm_kcalloc(dev, cfg->num_pcm_instances,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ sizeof(struct pcm_instance_info),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂ if (!card_info->pcm_instances) {
+ÂÂÂÂÂÂÂ ret = -ENOMEM;
+ÂÂÂÂÂÂÂ goto fail;
+ÂÂÂ }
+
+ÂÂÂ card_info->num_pcm_instances = cfg->num_pcm_instances;
+ÂÂÂ card_info->pcm_hw = cfg->pcm_hw;
+
+ÂÂÂ for (i = 0; i < cfg->num_pcm_instances; i++) {
+ÂÂÂÂÂÂÂ ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &card_info->pcm_instances[i]);
+ÂÂÂÂÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂÂÂÂÂ goto fail;
+ÂÂÂ }
+
+ÂÂÂ strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
+ÂÂÂ strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
+ÂÂÂ strncpy(card->longname, cfg->name_long, sizeof(card->longname));
+
+ÂÂÂ ret = snd_card_register(card);
+ÂÂÂ if (ret < 0)
+ÂÂÂÂÂÂÂ goto fail;
+
+ÂÂÂ return 0;
+
+fail:
+ÂÂÂ snd_card_free(card);
+ÂÂÂ return ret;
+}
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
+{
+ÂÂÂ struct card_info *card_info;
+ÂÂÂ struct snd_card *card;
+
+ÂÂÂ card_info = front_info->card_info;
+ÂÂÂ if (!card_info)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ card = card_info->card;
+ÂÂÂ if (!card)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
+ÂÂÂÂÂÂÂ card->number);
+ÂÂÂ snd_card_free(card);
+
+ÂÂÂ /* card_info will be freed when destroying front_info->xb_dev->dev */
+ÂÂÂ card_info->card = NULL;
+}
diff --git a/sound/xen/xen_snd_front_alsa.h b/sound/xen/xen_snd_front_alsa.h
new file mode 100644
index 000000000000..18abd9eec967
--- /dev/null
+++ b/sound/xen/xen_snd_front_alsa.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
+ */
+
+#ifndef __XEN_SND_FRONT_ALSA_H
+#define __XEN_SND_FRONT_ALSA_H
+
+struct xen_snd_front_info;
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u64 pos_bytes);
+
+#endif /* __XEN_SND_FRONT_ALSA_H */
diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c
index 9ece39f938f8..470196518716 100644
--- a/sound/xen/xen_snd_front_evtchnl.c
+++ b/sound/xen/xen_snd_front_evtchnl.c
@@ -14,6 +14,7 @@
 #include <xen/xenbus.h>
  #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
 #include "xen_snd_front_cfg.h"
 #include "xen_snd_front_evtchnl.h"
 @@ -111,7 +112,10 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
 Â switch (event->type) {
ÂÂÂÂÂÂÂÂÂ case XENSND_EVT_CUR_POS:
-ÂÂÂÂÂÂÂÂÂÂÂ /* do nothing at the moment */
+ spin_unlock_irqrestore(&front_info->io_lock, flags);
+ÂÂÂÂÂÂÂÂÂÂÂ xen_snd_front_alsa_handle_cur_pos(channel,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ event->op.cur_pos.position);
+ÂÂÂÂÂÂÂÂÂÂÂ spin_lock_irqsave(&front_info->io_lock, flags);
Is this correct? Why can you free the lock here without doing any
harm? What is the lock protecting? I'd like to see at least some
comments explaining why the lock is needed and why it can be dropped
here.
Well, this code has issues and will be re-worked:
1. Which locks I need:
1.1. lock to serialize requests to backend
1.2. lock to protect the ring: between requests code and interrupt handler

2. I don't really need a spinlock here b/c interrupts are threaded,
so I can safely use a mutex: thus no need for fancy code with unlocking
and locking again while servicing xen_snd_front_alsa_handle_cur_pos

Thank you

Juergen