Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1705191 > unrolled thread

[PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver

Started byOleksandr Andrushchenko <andr2000@gmail.com>
First post2017-08-07 09:50 +0200
Last post2017-08-07 13:40 +0200
Articles 18 — 3 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 01/11] ALSA: vsnd: Implement driver's probe/remove Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 06/11] ALSA: vsnd: Introduce ALSA virtual sound driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 10/11] ALSA: vsnd: Implement communication with backend Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 05/11] ALSA: vsnd: Implement handling of shared buffers Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 09/11] ALSA: vsnd: Implement ALSA PCM operations Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 02/11] ALSA: vsnd: Implement Xen bus state handling Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 07/11] ALSA: vsnd: Initialize virtul sound card Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
    [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 09:50 +0200
      Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Clemens Ladisch <clemens@ladisch.de> - 2017-08-07 12:40 +0200
        Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 13:40 +0200
          Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Clemens Ladisch <clemens@ladisch.de> - 2017-08-07 15:20 +0200
            Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 15:40 +0200
              Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Clemens Ladisch <clemens@ladisch.de> - 2017-08-07 16:00 +0200
                Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 17:20 +0200
                  Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period  interrupt emulation Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-08 08:10 +0200
    Re: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend  driver Takashi Sakamoto <o-takashi@sakamocchi.jp> - 2017-08-07 13:40 +0200
      Re: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend  driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 13:40 +0200

#1705191 — [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver
Message-ID<ubKXw-5k4-13@gated-at.bofh.it>
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

This patch series adds support for Xen [1] para-virtualized
sound frontend driver. It implements the protocol from
include/xen/interface/io/sndif.h with the following limitations:
- mute/unmute is not supported
- get/set volume is not supported
Volume control is not supported for the reason that most of the
use-cases (at the moment) are based on scenarious where
unprivileged OS (e.g. Android, AGL etc) use software mixers.

Both capture and playback are supported.

Thank you,
Oleksandr

[1] https://xenproject.org/

Oleksandr Andrushchenko (11):
  ALSA: vsnd: Implement driver's probe/remove
  ALSA: vsnd: Implement Xen bus state handling
  ALSA: vsnd: Read sound driver configuration from Xen store
  ALSA: vsnd: Implement Xen event channel handling
  ALSA: vsnd: Implement handling of shared buffers
  ALSA: vsnd: Introduce ALSA virtual sound driver
  ALSA: vsnd: Initialize virtul sound card
  ALSA: vsnd: Add timer for period interrupt emulation
  ALSA: vsnd: Implement ALSA PCM operations
  ALSA: vsnd: Implement communication with backend
  ALSA: vsnd: Introduce Kconfig option to enable Xen PV sound

 sound/drivers/Kconfig     |   12 +
 sound/drivers/Makefile    |    2 +
 sound/drivers/xen-front.c | 2029 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2043 insertions(+)

-- 
2.7.4

[toc] | [next] | [standalone]


#1705193 — [PATCH 01/11] ALSA: vsnd: Implement driver's probe/remove

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 01/11] ALSA: vsnd: Implement driver's probe/remove
Message-ID<ubKXw-5k4-33@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Add essential driver private info structure, initialize
locks and implement probe/remove of the Xen frontend
driver.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index 61779c36cae3..8c5de7b0e7b5 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -26,6 +26,16 @@
 
 #include <xen/interface/io/sndif.h>
 
+struct xdrv_info {
+	struct xenbus_device *xb_dev;
+	spinlock_t io_lock;
+	struct mutex mutex;
+};
+
+static void xdrv_remove_internal(struct xdrv_info *drv_info)
+{
+}
+
 static void xdrv_be_on_changed(struct xenbus_device *xb_dev,
 	enum xenbus_state backend_state)
 {
@@ -34,11 +44,28 @@ static void xdrv_be_on_changed(struct xenbus_device *xb_dev,
 static int xdrv_probe(struct xenbus_device *xb_dev,
 	const struct xenbus_device_id *id)
 {
+	struct xdrv_info *drv_info;
+
+	drv_info = devm_kzalloc(&xb_dev->dev, sizeof(*drv_info), GFP_KERNEL);
+	if (!drv_info) {
+		xenbus_dev_fatal(xb_dev, -ENOMEM, "allocating device memory");
+		return -ENOMEM;
+	}
+
+	drv_info->xb_dev = xb_dev;
+	spin_lock_init(&drv_info->io_lock);
+	mutex_init(&drv_info->mutex);
+	dev_set_drvdata(&xb_dev->dev, drv_info);
 	return 0;
 }
 
 static int xdrv_remove(struct xenbus_device *dev)
 {
+	struct xdrv_info *drv_info = dev_get_drvdata(&dev->dev);
+
+	mutex_lock(&drv_info->mutex);
+	xdrv_remove_internal(drv_info);
+	mutex_unlock(&drv_info->mutex);
 	return 0;
 }
 
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705194 — [PATCH 06/11] ALSA: vsnd: Introduce ALSA virtual sound driver

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 06/11] ALSA: vsnd: Introduce ALSA virtual sound driver
Message-ID<ubKXw-5k4-35@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

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

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 161 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 159 insertions(+), 2 deletions(-)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index 04ebc15757f4..f3e3f64f0aa6 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -19,13 +19,14 @@
  */
 
 #include <linux/module.h>
+#include <linux/platform_device.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 
-#include <xen/platform_pci.h>
 #include <xen/events.h>
 #include <xen/grant_table.h>
+#include <xen/platform_pci.h>
 #include <xen/xen.h>
 #include <xen/xenbus.h>
 
@@ -66,6 +67,33 @@ struct sh_buf_info {
 	size_t vbuffer_sz;
 };
 
+struct sdev_pcm_stream_info {
+	int unique_id;
+	struct snd_pcm_hardware pcm_hw;
+	struct xdrv_evtchnl_info *evt_chnl;
+	bool is_open;
+	uint8_t req_next_id;
+	struct sh_buf_info sh_buf;
+};
+
+struct sdev_pcm_instance_info {
+	struct sdev_card_info *card_info;
+	struct snd_pcm *pcm;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_streams_pb;
+	struct sdev_pcm_stream_info *streams_pb;
+	int num_pcm_streams_cap;
+	struct sdev_pcm_stream_info *streams_cap;
+};
+
+struct sdev_card_info {
+	struct xdrv_info *xdrv_info;
+	struct snd_card *card;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_instances;
+	struct sdev_pcm_instance_info *pcm_instances;
+};
+
 struct cfg_stream {
 	int unique_id;
 	char *xenstore_path;
@@ -99,6 +127,8 @@ struct xdrv_info {
 	struct xenbus_device *xb_dev;
 	spinlock_t io_lock;
 	struct mutex mutex;
+	bool sdrv_registered;
+	struct platform_device *sdrv_pdev;
 	int num_evt_channels;
 	struct xdrv_evtchnl_info *evt_chnls;
 	struct sdev_card_plat_data cfg_plat_data;
@@ -138,6 +168,132 @@ static struct snd_pcm_hardware sdrv_pcm_hw_default = {
 	.fifo_size = 0,
 };
 
+static int sdrv_new_pcm(struct sdev_card_info *card_info,
+	struct cfg_pcm_instance *instance_config,
+	struct sdev_pcm_instance_info *pcm_instance_info)
+{
+	return 0;
+}
+
+static int sdrv_probe(struct platform_device *pdev)
+{
+	struct sdev_card_info *card_info;
+	struct sdev_card_plat_data *platdata;
+	struct snd_card *card;
+	int ret, i;
+
+	platdata = dev_get_platdata(&pdev->dev);
+
+	dev_dbg(&pdev->dev, "Creating virtual sound card\n");
+
+	ret = snd_card_new(&pdev->dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
+		sizeof(struct sdev_card_info), &card);
+	if (ret < 0)
+		return ret;
+
+	card_info = card->private_data;
+	card_info->xdrv_info = platdata->xdrv_info;
+	card_info->card = card;
+	card_info->pcm_instances = devm_kcalloc(&pdev->dev,
+			platdata->cfg_card.num_pcm_instances,
+			sizeof(struct sdev_pcm_instance_info), GFP_KERNEL);
+	if (!card_info->pcm_instances) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	card_info->num_pcm_instances = platdata->cfg_card.num_pcm_instances;
+	card_info->pcm_hw = platdata->cfg_card.pcm_hw;
+
+	for (i = 0; i < platdata->cfg_card.num_pcm_instances; i++) {
+		ret = sdrv_new_pcm(card_info,
+			&platdata->cfg_card.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, platdata->cfg_card.name_short,
+		sizeof(card->shortname));
+	strncpy(card->longname, platdata->cfg_card.name_long,
+		sizeof(card->longname));
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto fail;
+
+	platform_set_drvdata(pdev, card);
+	return 0;
+
+fail:
+	snd_card_free(card);
+	return ret;
+}
+
+static int sdrv_remove(struct platform_device *pdev)
+{
+	struct sdev_card_info *info;
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	info = card->private_data;
+	dev_dbg(&pdev->dev, "Removing virtual sound card %d\n",
+		info->card->number);
+	snd_card_free(card);
+	return 0;
+}
+
+static struct platform_driver sdrv_info = {
+	.probe	= sdrv_probe,
+	.remove	= sdrv_remove,
+	.driver	= {
+		.name	= XENSND_DRIVER_NAME,
+	},
+};
+
+static void sdrv_cleanup(struct xdrv_info *drv_info)
+{
+	if (!drv_info->sdrv_registered)
+		return;
+
+	if (drv_info->sdrv_pdev) {
+		struct platform_device *sdrv_pdev;
+
+		sdrv_pdev = drv_info->sdrv_pdev;
+		if (sdrv_pdev)
+			platform_device_unregister(sdrv_pdev);
+	}
+	platform_driver_unregister(&sdrv_info);
+	drv_info->sdrv_registered = false;
+}
+
+static int sdrv_init(struct xdrv_info *drv_info)
+{
+	struct platform_device *sdrv_pdev;
+	int ret;
+
+	ret = platform_driver_register(&sdrv_info);
+	if (ret < 0)
+		return ret;
+
+	drv_info->sdrv_registered = true;
+	/* pass card configuration via platform data */
+	sdrv_pdev = platform_device_register_data(NULL,
+		XENSND_DRIVER_NAME, 0, &drv_info->cfg_plat_data,
+		sizeof(drv_info->cfg_plat_data));
+	if (IS_ERR(sdrv_pdev))
+		goto fail;
+
+	drv_info->sdrv_pdev = sdrv_pdev;
+	return 0;
+
+fail:
+	dev_err(&drv_info->xb_dev->dev,
+		"failed to register virtual sound driver\n");
+	sdrv_cleanup(drv_info);
+	return -ENODEV;
+}
+
 static irqreturn_t xdrv_evtchnl_interrupt(int irq, void *dev_id)
 {
 	struct xdrv_evtchnl_info *channel = dev_id;
@@ -830,6 +986,7 @@ static int cfg_card(struct xdrv_info *drv_info,
 
 static void xdrv_remove_internal(struct xdrv_info *drv_info)
 {
+	sdrv_cleanup(drv_info);
 	xdrv_evtchnl_free_all(drv_info);
 }
 
@@ -1018,7 +1175,7 @@ static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
 
 static inline int xdrv_be_on_connected(struct xdrv_info *drv_info)
 {
-	return 0;
+	return sdrv_init(drv_info);
 }
 
 static inline void xdrv_be_on_disconnected(struct xdrv_info *drv_info)
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705195 — [PATCH 10/11] ALSA: vsnd: Implement communication with backend

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 10/11] ALSA: vsnd: Implement communication with backend
Message-ID<ubKXx-5k4-39@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Implement frontend to backend communication according to
the para-virtualized sound protocol: xen/interface/io/sndif.h.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 302 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 288 insertions(+), 14 deletions(-)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index 7275e9cb38c0..8bfec43ef03a 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -38,6 +38,8 @@
  * because of the fact it is already in use/reserved by the PV console.
  */
 #define GRANT_INVALID_REF	0
+/* timeout in ms to wait for backend to respond */
+#define VSND_WAIT_BACK_MS	3000
 /* maximum number of supported streams */
 #define VSND_MAX_STREAM		8
 
@@ -151,10 +153,12 @@ struct xdrv_info {
 	struct sdev_card_plat_data cfg_plat_data;
 };
 
+static inline void xdrv_evtchnl_flush(struct xdrv_evtchnl_info *channel);
 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 grant_ref_t sh_buf_get_dir_start(struct sh_buf_info *buf);
 
 static struct sdev_pcm_stream_info *sdrv_stream_get(
 	struct snd_pcm_substream *substream)
@@ -314,6 +318,234 @@ static void sdrv_copy_pcm_hw(struct snd_pcm_hardware *dst,
 	}
 }
 
+struct ALSA_SNDIF_SAMPLE_FORMAT {
+	uint8_t sndif;
+	snd_pcm_format_t alsa;
+};
+
+static 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 alsa_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 void sdrv_stream_clear(struct sdev_pcm_stream_info *stream)
+{
+	stream->is_open = false;
+	stream->req_next_id = 0;
+	sh_buf_clear(&stream->sh_buf);
+}
+
+static struct xensnd_req *sdrv_be_stream_prepare_req(
+	struct sdev_pcm_stream_info *stream, uint8_t operation)
+{
+	struct xensnd_req *req;
+
+	req = RING_GET_REQUEST(&stream->evt_chnl->ring,
+		stream->evt_chnl->ring.req_prod_pvt);
+	req->operation = operation;
+	req->id = stream->req_next_id++;
+	stream->evt_chnl->resp_id = req->id;
+	return req;
+}
+
+static void sdrv_be_stream_free(struct sdev_pcm_stream_info *stream)
+{
+	sh_buf_free(&stream->sh_buf);
+	sdrv_stream_clear(stream);
+}
+
+static int sdrv_be_stream_do_io(struct xdrv_evtchnl_info *evtchnl)
+{
+	if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+		return -EIO;
+
+	reinit_completion(&evtchnl->completion);
+	xdrv_evtchnl_flush(evtchnl);
+	return 0;
+}
+
+static inline int sdrv_be_stream_wait_io(struct xdrv_evtchnl_info *evtchnl)
+{
+	if (wait_for_completion_timeout(
+			&evtchnl->completion,
+			msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int sdrv_be_stream_open(struct snd_pcm_substream *substream,
+	struct sdev_pcm_stream_info *stream)
+{
+	struct sdev_pcm_instance_info *pcm_instance =
+		snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct xdrv_info *xdrv_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	xdrv_info = pcm_instance->card_info->xdrv_info;
+
+	ret = alsa_to_sndif_format(runtime->format);
+	if (ret < 0) {
+		dev_err(&xdrv_info->xb_dev->dev,
+			"Unsupported sample format: %d\n", runtime->format);
+		return ret;
+	}
+
+	spin_lock_irqsave(&xdrv_info->io_lock, flags);
+	stream->is_open = false;
+	req = sdrv_be_stream_prepare_req(stream, XENSND_OP_OPEN);
+	req->op.open.pcm_format = (uint8_t)ret;
+	req->op.open.pcm_channels = runtime->channels;
+	req->op.open.pcm_rate = runtime->rate;
+	req->op.open.buffer_sz = stream->sh_buf.vbuffer_sz;
+	req->op.open.gref_directory = sh_buf_get_dir_start(&stream->sh_buf);
+
+	ret = sdrv_be_stream_do_io(stream->evt_chnl);
+	spin_unlock_irqrestore(&xdrv_info->io_lock, flags);
+
+	if (ret < 0)
+		return ret;
+
+	ret = sdrv_be_stream_wait_io(stream->evt_chnl);
+	stream->is_open = ret < 0 ? false : true;
+	return ret;
+}
+
+static int sdrv_be_stream_close(struct snd_pcm_substream *substream,
+	struct sdev_pcm_stream_info *stream)
+{
+	struct sdev_pcm_instance_info *pcm_instance =
+		snd_pcm_substream_chip(substream);
+	struct xdrv_info *xdrv_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	xdrv_info = pcm_instance->card_info->xdrv_info;
+
+	spin_lock_irqsave(&xdrv_info->io_lock, flags);
+	stream->is_open = false;
+	req = sdrv_be_stream_prepare_req(stream, XENSND_OP_CLOSE);
+
+	ret = sdrv_be_stream_do_io(stream->evt_chnl);
+	spin_unlock_irqrestore(&xdrv_info->io_lock, flags);
+
+	if (ret < 0)
+		return ret;
+
+	return sdrv_be_stream_wait_io(stream->evt_chnl);
+}
+
 static int sdrv_alsa_open(struct snd_pcm_substream *substream)
 {
 	struct sdev_pcm_instance_info *pcm_instance =
@@ -339,7 +571,7 @@ static int sdrv_alsa_open(struct snd_pcm_substream *substream)
 	ret = sdrv_alsa_timer_create(substream);
 
 	spin_lock_irqsave(&xdrv_info->io_lock, flags);
-	sh_buf_clear(&stream->sh_buf);
+	sdrv_stream_clear(stream);
 	stream->evt_chnl = &xdrv_info->evt_chnls[stream->unique_id];
 	if (ret < 0)
 		stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED;
@@ -378,7 +610,7 @@ static int sdrv_alsa_hw_params(struct snd_pcm_substream *substream,
 	int ret;
 
 	buffer_size = params_buffer_bytes(params);
-	sh_buf_clear(&stream->sh_buf);
+	sdrv_stream_clear(stream);
 	xdrv_info = pcm_instance->card_info->xdrv_info;
 	ret = sh_buf_alloc(xdrv_info->xb_dev,
 		&stream->sh_buf, buffer_size);
@@ -390,22 +622,18 @@ static int sdrv_alsa_hw_params(struct snd_pcm_substream *substream,
 	dev_err(&xdrv_info->xb_dev->dev,
 		"Failed to allocate buffers for stream idx %d\n",
 		stream->unique_id);
+	sdrv_be_stream_free(stream);
 	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;
+	int ret;
 
-	xdrv_info = pcm_instance->card_info->xdrv_info;
-	spin_lock_irqsave(&xdrv_info->io_lock, flags);
-	sh_buf_free(&stream->sh_buf);
-	spin_unlock_irqrestore(&xdrv_info->io_lock, flags);
-	return 0;
+	ret = sdrv_be_stream_close(substream, stream);
+	sdrv_be_stream_free(stream);
+	return ret;
 }
 
 static int sdrv_alsa_prepare(struct snd_pcm_substream *substream)
@@ -413,8 +641,12 @@ static int sdrv_alsa_prepare(struct snd_pcm_substream *substream)
 	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
 	int ret = 0;
 
-	if (!stream->is_open)
+	if (!stream->is_open) {
+		ret = sdrv_be_stream_open(substream, stream);
+		if (ret < 0)
+			return ret;
 		ret = sdrv_alsa_timer_prepare(substream);
+	}
 	return ret;
 }
 
@@ -446,7 +678,28 @@ static inline snd_pcm_uframes_t sdrv_alsa_pointer(
 static int sdrv_alsa_playback_do_write(struct snd_pcm_substream *substream,
 	unsigned long pos, unsigned long count)
 {
-	return 0;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_pcm_instance_info *pcm_instance =
+		snd_pcm_substream_chip(substream);
+	struct xdrv_info *xdrv_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	xdrv_info = pcm_instance->card_info->xdrv_info;
+
+	spin_lock_irqsave(&xdrv_info->io_lock, flags);
+	req = sdrv_be_stream_prepare_req(stream, XENSND_OP_WRITE);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = sdrv_be_stream_do_io(stream->evt_chnl);
+	spin_unlock_irqrestore(&xdrv_info->io_lock, flags);
+
+	if (ret < 0)
+		return ret;
+
+	return sdrv_be_stream_wait_io(stream->evt_chnl);
 }
 
 static int sdrv_alsa_playback_copy_user(struct snd_pcm_substream *substream,
@@ -479,7 +732,28 @@ static int sdrv_alsa_playback_copy_kernel(struct snd_pcm_substream *substream,
 static int sdrv_alsa_playback_do_read(struct snd_pcm_substream *substream,
 	unsigned long pos, unsigned long count)
 {
-	return 0;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_pcm_instance_info *pcm_instance =
+		snd_pcm_substream_chip(substream);
+	struct xdrv_info *xdrv_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	xdrv_info = pcm_instance->card_info->xdrv_info;
+
+	spin_lock_irqsave(&xdrv_info->io_lock, flags);
+	req = sdrv_be_stream_prepare_req(stream, XENSND_OP_READ);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = sdrv_be_stream_do_io(stream->evt_chnl);
+	spin_unlock_irqrestore(&xdrv_info->io_lock, flags);
+
+	if (ret < 0)
+		return ret;
+
+	return sdrv_be_stream_wait_io(stream->evt_chnl);
 }
 
 static int sdrv_alsa_capture_copy_user(struct snd_pcm_substream *substream,
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705197 — [PATCH 05/11] ALSA: vsnd: Implement handling of shared buffers

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 05/11] ALSA: vsnd: Implement handling of shared buffers
Message-ID<ubKXx-5k4-43@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Implement shared buffer handling according to the
para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index a92459b2737e..04ebc15757f4 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -58,6 +58,14 @@ struct xdrv_evtchnl_info {
 	uint16_t resp_id;
 };
 
+struct sh_buf_info {
+	int num_grefs;
+	grant_ref_t *grefs;
+	uint8_t *vdirectory;
+	uint8_t *vbuffer;
+	size_t vbuffer_sz;
+};
+
 struct cfg_stream {
 	int unique_id;
 	char *xenstore_path;
@@ -825,6 +833,176 @@ static void xdrv_remove_internal(struct xdrv_info *drv_info)
 	xdrv_evtchnl_free_all(drv_info);
 }
 
+static inline grant_ref_t sh_buf_get_dir_start(struct sh_buf_info *buf)
+{
+	if (!buf->grefs)
+		return GRANT_INVALID_REF;
+	return buf->grefs[0];
+}
+
+static inline void sh_buf_clear(struct sh_buf_info *buf)
+{
+	memset(buf, 0, sizeof(*buf));
+}
+
+static void sh_buf_free(struct sh_buf_info *buf)
+{
+	int i;
+
+	if (buf->grefs) {
+		for (i = 0; i < buf->num_grefs; i++)
+			if (buf->grefs[i] != GRANT_INVALID_REF)
+				gnttab_end_foreign_access(buf->grefs[i],
+						0, 0UL);
+		kfree(buf->grefs);
+	}
+	kfree(buf->vdirectory);
+	free_pages_exact(buf->vbuffer, buf->vbuffer_sz);
+	sh_buf_clear(buf);
+}
+
+/*
+ * number of grant references a page can hold with respect to the
+ * xendispl_page_directory header
+ */
+#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
+	offsetof(struct xensnd_page_directory, gref)) / \
+	sizeof(grant_ref_t))
+
+static void sh_buf_fill_page_dir(struct sh_buf_info *buf, int num_pages_dir)
+{
+	struct xensnd_page_directory *page_dir;
+	unsigned char *ptr;
+	int i, cur_gref, grefs_left, to_copy;
+
+	ptr = buf->vdirectory;
+	grefs_left = buf->num_grefs - num_pages_dir;
+	/*
+	 * skip grant references at the beginning, they are for pages granted
+	 * for the page directory itself
+	 */
+	cur_gref = num_pages_dir;
+	for (i = 0; i < num_pages_dir; i++) {
+		page_dir = (struct xensnd_page_directory *)ptr;
+		if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
+			to_copy = grefs_left;
+			page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+		} else {
+			to_copy = XENSND_NUM_GREFS_PER_PAGE;
+			page_dir->gref_dir_next_page = buf->grefs[i + 1];
+		}
+		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+			to_copy * sizeof(grant_ref_t));
+		ptr += XEN_PAGE_SIZE;
+		grefs_left -= to_copy;
+		cur_gref += to_copy;
+	}
+}
+
+static int sh_buf_grant_refs(struct xenbus_device *xb_dev,
+	struct sh_buf_info *buf,
+	int num_pages_dir, int num_pages_vbuffer, int num_grefs)
+{
+	grant_ref_t priv_gref_head;
+	int ret, i, j, cur_ref;
+	int otherend_id;
+
+	ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
+	if (ret)
+		return ret;
+
+	buf->num_grefs = num_grefs;
+	otherend_id = xb_dev->otherend_id;
+	j = 0;
+
+	for (i = 0; i < num_pages_dir; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id,
+			xen_page_to_gfn(virt_to_page(buf->vdirectory +
+				XEN_PAGE_SIZE * i)), 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	for (i = 0; i < num_pages_vbuffer; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id,
+			xen_page_to_gfn(virt_to_page(buf->vbuffer +
+				XEN_PAGE_SIZE * i)), 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	gnttab_free_grant_references(priv_gref_head);
+	sh_buf_fill_page_dir(buf, num_pages_dir);
+	return 0;
+
+fail:
+	gnttab_free_grant_references(priv_gref_head);
+	return ret;
+}
+
+static int sh_buf_alloc_int_buffers(struct sh_buf_info *buf,
+		int num_pages_dir, int num_pages_vbuffer, int num_grefs)
+{
+	buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+	if (!buf->grefs)
+		return -ENOMEM;
+
+	buf->vdirectory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+	if (!buf->vdirectory)
+		goto fail;
+
+	buf->vbuffer_sz = num_pages_vbuffer * XEN_PAGE_SIZE;
+	buf->vbuffer = alloc_pages_exact(buf->vbuffer_sz, GFP_KERNEL);
+	if (!buf->vbuffer)
+		goto fail;
+	return 0;
+
+fail:
+	kfree(buf->grefs);
+	buf->grefs = NULL;
+	kfree(buf->vdirectory);
+	buf->vdirectory = NULL;
+	return -ENOMEM;
+}
+
+static int sh_buf_alloc(struct xenbus_device *xb_dev,
+	struct sh_buf_info *buf, unsigned int buffer_size)
+{
+	int num_pages_vbuffer, num_pages_dir, num_grefs;
+	int ret;
+
+	sh_buf_clear(buf);
+
+	num_pages_vbuffer = DIV_ROUND_UP(buffer_size, XEN_PAGE_SIZE);
+	/* number of pages the page directory consumes itself */
+	num_pages_dir = DIV_ROUND_UP(num_pages_vbuffer,
+		XENSND_NUM_GREFS_PER_PAGE);
+	num_grefs = num_pages_vbuffer + num_pages_dir;
+
+	ret = sh_buf_alloc_int_buffers(buf, num_pages_dir,
+		num_pages_vbuffer, num_grefs);
+	if (ret < 0)
+		return ret;
+
+	ret = sh_buf_grant_refs(xb_dev, buf,
+		num_pages_dir, num_pages_vbuffer, num_grefs);
+	if (ret < 0)
+		return ret;
+
+	sh_buf_fill_page_dir(buf, num_pages_dir);
+	return 0;
+}
+
 static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
 {
 	int stream_idx;
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705198 — [PATCH 09/11] ALSA: vsnd: Implement ALSA PCM operations

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 09/11] ALSA: vsnd: Implement ALSA PCM operations
Message-ID<ubKXx-5k4-41@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

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 <oleksandr_andrushchenko@epam.com>
---
 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(&runtime->hw, &stream->pcm_hw, &pcm_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(&xdrv_info->io_lock, flags);
+	sh_buf_clear(&stream->sh_buf);
+	stream->evt_chnl = &xdrv_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(&xdrv_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(&xdrv_info->io_lock, flags);
+	stream->evt_chnl->state = EVTCHNL_STATE_DISCONNECTED;
+	spin_unlock_irqrestore(&xdrv_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(&stream->sh_buf);
+	xdrv_info = pcm_instance->card_info->xdrv_info;
+	ret = sh_buf_alloc(xdrv_info->xb_dev,
+		&stream->sh_buf, buffer_size);
+	if (ret < 0)
+		goto fail;
 	return 0;
+
+fail:
+	dev_err(&xdrv_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(&xdrv_info->io_lock, flags);
+	sh_buf_free(&stream->sh_buf);
+	spin_unlock_irqrestore(&xdrv_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:
+		/* fall through */
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		return sdrv_alsa_timer_stop(substream);
+
+	default:
+		break;
+	}
 	return 0;
 }
 
 static inline snd_pcm_uframes_t sdrv_alsa_pointer(
 	struct snd_pcm_substream *substream)
 {
+	return sdrv_alsa_timer_pointer(substream);
+}
+
+static int sdrv_alsa_playback_do_write(struct snd_pcm_substream *substream,
+	unsigned long pos, unsigned long count)
+{
 	return 0;
 }
 
 static int sdrv_alsa_playback_copy_user(struct snd_pcm_substream *substream,
-		int channel, unsigned long pos, void __user *buf,
-		unsigned long bytes)
+		int channel, unsigned long pos, void __user *src,
+		unsigned long count)
 {
-	return 0;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.vbuffer_sz))
+		return -EINVAL;
+
+	if (copy_from_user(stream->sh_buf.vbuffer + pos, src, count))
+		return -EFAULT;
+
+	return sdrv_alsa_playback_do_write(substream, pos, count);
 }
 
 static int sdrv_alsa_playback_copy_kernel(struct snd_pcm_substream *substream,
-		int channel, unsigned long pos, void *buf, unsigned long bytes)
+		int channel, unsigned long pos, void *src, unsigned long count)
+{
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.vbuffer_sz))
+		return -EINVAL;
+
+	memcpy(stream->sh_buf.vbuffer + pos, src, count);
+	return sdrv_alsa_playback_do_write(substream, pos, count);
+}
+
+static int sdrv_alsa_playback_do_read(struct snd_pcm_substream *substream,
+	unsigned long pos, unsigned long count)
 {
 	return 0;
 }
 
 static int sdrv_alsa_capture_copy_user(struct snd_pcm_substream *substream,
-		int channel, unsigned long pos, void __user *buf,
-		unsigned long bytes)
+		int channel, unsigned long pos, void __user *dst,
+		unsigned long count)
 {
-	return 0;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.vbuffer_sz))
+		return -EINVAL;
+
+	ret = sdrv_alsa_playback_do_read(substream, pos, count);
+	if (ret < 0)
+		return ret;
+
+	return copy_to_user(dst, stream->sh_buf.vbuffer + pos, count) ?
+		-EFAULT : 0;
 }
 
 static int sdrv_alsa_capture_copy_kernel(struct snd_pcm_substream *substream,
-		int channel, unsigned long pos, void *buf, unsigned long bytes)
+		int channel, unsigned long pos, void *dst, unsigned long count)
 {
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.vbuffer_sz))
+		return -EINVAL;
+
+	ret = sdrv_alsa_playback_do_read(substream, pos, count);
+	if (ret < 0)
+		return ret;
+
+	memcpy(dst, stream->sh_buf.vbuffer + pos, count);
 	return 0;
 }
 
 static int sdrv_alsa_playback_fill_silence(struct snd_pcm_substream *substream,
-	int channel, unsigned long pos, unsigned long bytes)
+	int channel, unsigned long pos, unsigned long count)
 {
-	return 0;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.vbuffer_sz))
+		return -EINVAL;
+
+	memset(stream->sh_buf.vbuffer + pos, 0, count);
+	return sdrv_alsa_playback_do_write(substream, pos, count);
 }
 
 #define MAX_XEN_BUFFER_SIZE	(64 * 1024)
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705199 — [PATCH 02/11] ALSA: vsnd: Implement Xen bus state handling

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 02/11] ALSA: vsnd: Implement Xen bus state handling
Message-ID<ubKXx-5k4-49@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Initial handling for Xen bus states: implement
Xen bus state machine for the front driver according to
the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index 8c5de7b0e7b5..c4fd21cac3a7 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -36,9 +36,99 @@ static void xdrv_remove_internal(struct xdrv_info *drv_info)
 {
 }
 
+static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
+{
+	return 0;
+}
+
+static inline int xdrv_be_on_connected(struct xdrv_info *drv_info)
+{
+	return 0;
+}
+
+static inline void xdrv_be_on_disconnected(struct xdrv_info *drv_info)
+{
+	xdrv_remove_internal(drv_info);
+}
+
 static void xdrv_be_on_changed(struct xenbus_device *xb_dev,
 	enum xenbus_state backend_state)
 {
+	struct xdrv_info *drv_info = dev_get_drvdata(&xb_dev->dev);
+	int ret;
+
+	switch (backend_state) {
+	case XenbusStateReconfiguring:
+		/* fall through */
+	case XenbusStateReconfigured:
+		/* fall through */
+	case XenbusStateInitialised:
+		/* fall through */
+		break;
+
+	case XenbusStateInitialising:
+		if (xb_dev->state == XenbusStateInitialising)
+			break;
+
+		/* recovering after backend unexpected closure */
+		mutex_lock(&drv_info->mutex);
+		xdrv_be_on_disconnected(drv_info);
+		mutex_unlock(&drv_info->mutex);
+		xenbus_switch_state(xb_dev, XenbusStateInitialising);
+		break;
+
+	case XenbusStateInitWait:
+		if (xb_dev->state != XenbusStateInitialising)
+			break;
+
+		mutex_lock(&drv_info->mutex);
+		ret = xdrv_be_on_initwait(drv_info);
+		mutex_unlock(&drv_info->mutex);
+		if (ret < 0) {
+			xenbus_dev_fatal(xb_dev, ret,
+				"initializing " XENSND_DRIVER_NAME);
+			break;
+		}
+
+		xenbus_switch_state(xb_dev, XenbusStateInitialised);
+		break;
+
+	case XenbusStateConnected:
+		if (xb_dev->state != XenbusStateInitialised)
+			break;
+
+		mutex_lock(&drv_info->mutex);
+		ret = xdrv_be_on_connected(drv_info);
+		mutex_unlock(&drv_info->mutex);
+		if (ret < 0) {
+			xenbus_dev_fatal(xb_dev, ret,
+				"connecting " XENSND_DRIVER_NAME);
+			break;
+		}
+
+		xenbus_switch_state(xb_dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		/*
+		 * in this state backend starts freeing resources,
+		 * so let it go into closed state first, so we can also
+		 * remove ours
+		 */
+		break;
+
+	case XenbusStateUnknown:
+		/* fall through */
+	case XenbusStateClosed:
+		if (xb_dev->state == XenbusStateClosed)
+			break;
+
+		mutex_lock(&drv_info->mutex);
+		xdrv_be_on_disconnected(drv_info);
+		mutex_unlock(&drv_info->mutex);
+		xenbus_switch_state(xb_dev, XenbusStateInitialising);
+		break;
+	}
 }
 
 static int xdrv_probe(struct xenbus_device *xb_dev,
@@ -56,6 +146,7 @@ static int xdrv_probe(struct xenbus_device *xb_dev,
 	spin_lock_init(&drv_info->io_lock);
 	mutex_init(&drv_info->mutex);
 	dev_set_drvdata(&xb_dev->dev, drv_info);
+	xenbus_switch_state(xb_dev, XenbusStateInitialising);
 	return 0;
 }
 
@@ -63,6 +154,7 @@ static int xdrv_remove(struct xenbus_device *dev)
 {
 	struct xdrv_info *drv_info = dev_get_drvdata(&dev->dev);
 
+	xenbus_switch_state(dev, XenbusStateClosed);
 	mutex_lock(&drv_info->mutex);
 	xdrv_remove_internal(drv_info);
 	mutex_unlock(&drv_info->mutex);
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705200 — [PATCH 07/11] ALSA: vsnd: Initialize virtul sound card

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 07/11] ALSA: vsnd: Initialize virtul sound card
Message-ID<ubKXx-5k4-47@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Initialize virtual sound card with streams according to the
Xen store configuration.
Add stubs for stream PCM operations.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 232 insertions(+)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index f3e3f64f0aa6..9f31e6832086 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -134,6 +134,129 @@ struct xdrv_info {
 	struct sdev_card_plat_data cfg_plat_data;
 };
 
+static struct sdev_pcm_stream_info *sdrv_stream_get(
+	struct snd_pcm_substream *substream)
+{
+	struct sdev_pcm_instance_info *pcm_instance =
+		snd_pcm_substream_chip(substream);
+	struct sdev_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 void sdrv_copy_pcm_hw(struct snd_pcm_hardware *dst,
+	struct snd_pcm_hardware *src,
+	struct snd_pcm_hardware *ref_pcm_hw)
+{
+	*dst = *ref_pcm_hw;
+
+	if (src->formats)
+		dst->formats = src->formats;
+	if (src->buffer_bytes_max)
+		dst->buffer_bytes_max =
+			src->buffer_bytes_max;
+	if (src->period_bytes_min)
+		dst->period_bytes_min =
+			src->period_bytes_min;
+	if (src->period_bytes_max)
+		dst->period_bytes_max =
+			src->period_bytes_max;
+	if (src->periods_min)
+		dst->periods_min = src->periods_min;
+	if (src->periods_max)
+		dst->periods_max = src->periods_max;
+	if (src->rates)
+		dst->rates = src->rates;
+	if (src->rate_min)
+		dst->rate_min = src->rate_min;
+	if (src->rate_max)
+		dst->rate_max = src->rate_max;
+	if (src->channels_min)
+		dst->channels_min = src->channels_min;
+	if (src->channels_max)
+		dst->channels_max = src->channels_max;
+	if (src->buffer_bytes_max) {
+		dst->buffer_bytes_max = src->buffer_bytes_max;
+		dst->period_bytes_max = src->buffer_bytes_max /
+			src->periods_max;
+		dst->periods_max = dst->buffer_bytes_max /
+			dst->period_bytes_max;
+	}
+}
+
+static int sdrv_alsa_open(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int sdrv_alsa_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int sdrv_alsa_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return 0;
+}
+
+static int sdrv_alsa_hw_free(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int sdrv_alsa_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int sdrv_alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return 0;
+}
+
+static inline snd_pcm_uframes_t sdrv_alsa_pointer(
+	struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int sdrv_alsa_playback_copy_user(struct snd_pcm_substream *substream,
+		int channel, unsigned long pos, void __user *buf,
+		unsigned long bytes)
+{
+	return 0;
+}
+
+static int sdrv_alsa_playback_copy_kernel(struct snd_pcm_substream *substream,
+		int channel, unsigned long pos, void *buf, unsigned long bytes)
+{
+	return 0;
+}
+
+static int sdrv_alsa_capture_copy_user(struct snd_pcm_substream *substream,
+		int channel, unsigned long pos, void __user *buf,
+		unsigned long bytes)
+{
+	return 0;
+}
+
+static int sdrv_alsa_capture_copy_kernel(struct snd_pcm_substream *substream,
+		int channel, unsigned long pos, void *buf, unsigned long bytes)
+{
+	return 0;
+}
+
+static int sdrv_alsa_playback_fill_silence(struct snd_pcm_substream *substream,
+	int channel, unsigned long pos, unsigned long bytes)
+{
+	return 0;
+}
+
 #define MAX_XEN_BUFFER_SIZE	(64 * 1024)
 #define MAX_BUFFER_SIZE		MAX_XEN_BUFFER_SIZE
 #define MIN_PERIOD_SIZE		64
@@ -168,10 +291,119 @@ static struct snd_pcm_hardware sdrv_pcm_hw_default = {
 	.fifo_size = 0,
 };
 
+/*
+ * 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 sdrv_alsa_playback_ops = {
+	.open = sdrv_alsa_open,
+	.close = sdrv_alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = sdrv_alsa_hw_params,
+	.hw_free = sdrv_alsa_hw_free,
+	.prepare = sdrv_alsa_prepare,
+	.trigger = sdrv_alsa_trigger,
+	.pointer = sdrv_alsa_pointer,
+	.copy_user = sdrv_alsa_playback_copy_user,
+	.copy_kernel = sdrv_alsa_playback_copy_kernel,
+	.fill_silence = sdrv_alsa_playback_fill_silence,
+};
+
+static struct snd_pcm_ops sdrv_alsa_capture_ops = {
+	.open = sdrv_alsa_open,
+	.close = sdrv_alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = sdrv_alsa_hw_params,
+	.hw_free = sdrv_alsa_hw_free,
+	.prepare = sdrv_alsa_prepare,
+	.trigger = sdrv_alsa_trigger,
+	.pointer = sdrv_alsa_pointer,
+	.copy_user = sdrv_alsa_capture_copy_user,
+	.copy_kernel = sdrv_alsa_capture_copy_kernel,
+};
+
 static int sdrv_new_pcm(struct sdev_card_info *card_info,
 	struct cfg_pcm_instance *instance_config,
 	struct sdev_pcm_instance_info *pcm_instance_info)
 {
+	struct snd_pcm *pcm;
+	int ret, i;
+
+	dev_dbg(&card_info->xdrv_info->xb_dev->dev,
+		"New PCM device \"%s\" with id %d playback %d capture %d",
+		instance_config->name,
+		instance_config->device_id,
+		instance_config->num_streams_pb,
+		instance_config->num_streams_cap);
+
+	pcm_instance_info->card_info = card_info;
+
+	sdrv_copy_pcm_hw(&pcm_instance_info->pcm_hw,
+		&instance_config->pcm_hw, &card_info->pcm_hw);
+
+	if (instance_config->num_streams_pb) {
+		pcm_instance_info->streams_pb = devm_kcalloc(
+			&card_info->card->card_dev,
+			instance_config->num_streams_pb,
+			sizeof(struct sdev_pcm_stream_info),
+			GFP_KERNEL);
+		if (!pcm_instance_info->streams_pb)
+			return -ENOMEM;
+	}
+
+	if (instance_config->num_streams_cap) {
+		pcm_instance_info->streams_cap = devm_kcalloc(
+			&card_info->card->card_dev,
+			instance_config->num_streams_cap,
+			sizeof(struct sdev_pcm_stream_info),
+			GFP_KERNEL);
+		if (!pcm_instance_info->streams_cap)
+			return -ENOMEM;
+	}
+
+	pcm_instance_info->num_pcm_streams_pb =
+			instance_config->num_streams_pb;
+	pcm_instance_info->num_pcm_streams_cap =
+			instance_config->num_streams_cap;
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
+		pcm_instance_info->streams_pb[i].pcm_hw =
+			instance_config->streams_pb[i].pcm_hw;
+		pcm_instance_info->streams_pb[i].unique_id =
+			instance_config->streams_pb[i].unique_id;
+	}
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
+		pcm_instance_info->streams_cap[i].pcm_hw =
+			instance_config->streams_cap[i].pcm_hw;
+		pcm_instance_info->streams_cap[i].unique_id =
+			instance_config->streams_cap[i].unique_id;
+	}
+
+	ret = snd_pcm_new(card_info->card, instance_config->name,
+			instance_config->device_id,
+			instance_config->num_streams_pb,
+			instance_config->num_streams_cap,
+			&pcm);
+	if (ret < 0)
+		return ret;
+
+	pcm->private_data = pcm_instance_info;
+	pcm->info_flags = 0;
+	strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+
+	if (instance_config->num_streams_pb)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&sdrv_alsa_playback_ops);
+
+	if (instance_config->num_streams_cap)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&sdrv_alsa_capture_ops);
+
+	pcm_instance_info->pcm = pcm;
 	return 0;
 }
 
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705203 — [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 09:50 +0200
Subject[PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubKXx-5k4-51@gated-at.bofh.it>
In reply to#1705191
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

Front sound driver has no real interrupts, so
playback/capture period passed interrupt needs to be emulated:
this is done via timer. Add required timer operations,
this is based on sound/drivers/dummy.c.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
 sound/drivers/xen-front.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index 9f31e6832086..507c5eb343c8 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -67,12 +67,29 @@ struct sh_buf_info {
 	size_t vbuffer_sz;
 };
 
+struct sdev_alsa_timer_info {
+	spinlock_t lock;
+	struct timer_list timer;
+	unsigned long base_time;
+	/* fractional sample position (based HZ) */
+	unsigned int frac_pos;
+	unsigned int frac_period_rest;
+	/* buffer_size * HZ */
+	unsigned int frac_buffer_size;
+	/* period_size * HZ */
+	unsigned int frac_period_size;
+	unsigned int rate;
+	int elapsed;
+	struct snd_pcm_substream *substream;
+};
+
 struct sdev_pcm_stream_info {
 	int unique_id;
 	struct snd_pcm_hardware pcm_hw;
 	struct xdrv_evtchnl_info *evt_chnl;
 	bool is_open;
 	uint8_t req_next_id;
+	struct sdev_alsa_timer_info timer;
 	struct sh_buf_info sh_buf;
 };
 
@@ -148,6 +165,110 @@ static struct sdev_pcm_stream_info *sdrv_stream_get(
 	return stream;
 }
 
+static inline void sdrv_alsa_timer_rearm(struct sdev_alsa_timer_info *dpcm)
+{
+	mod_timer(&dpcm->timer, jiffies +
+		(dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate);
+}
+
+static void sdrv_alsa_timer_update(struct sdev_alsa_timer_info *dpcm)
+{
+	unsigned long delta;
+
+	delta = jiffies - dpcm->base_time;
+	if (!delta)
+		return;
+	dpcm->base_time += delta;
+	delta *= dpcm->rate;
+	dpcm->frac_pos += delta;
+	while (dpcm->frac_pos >= dpcm->frac_buffer_size)
+		dpcm->frac_pos -= dpcm->frac_buffer_size;
+	while (dpcm->frac_period_rest <= delta) {
+		dpcm->elapsed++;
+		dpcm->frac_period_rest += dpcm->frac_period_size;
+	}
+	dpcm->frac_period_rest -= delta;
+}
+
+static int sdrv_alsa_timer_start(struct snd_pcm_substream *substream)
+{
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_alsa_timer_info *dpcm = &stream->timer;
+
+	spin_lock(&dpcm->lock);
+	dpcm->base_time = jiffies;
+	sdrv_alsa_timer_rearm(dpcm);
+	spin_unlock(&dpcm->lock);
+	return 0;
+}
+
+static int sdrv_alsa_timer_stop(struct snd_pcm_substream *substream)
+{
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_alsa_timer_info *dpcm = &stream->timer;
+
+	spin_lock(&dpcm->lock);
+	del_timer(&dpcm->timer);
+	spin_unlock(&dpcm->lock);
+	return 0;
+}
+
+static int sdrv_alsa_timer_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_alsa_timer_info *dpcm = &stream->timer;
+
+	dpcm->frac_pos = 0;
+	dpcm->rate = runtime->rate;
+	dpcm->frac_buffer_size = runtime->buffer_size * HZ;
+	dpcm->frac_period_size = runtime->period_size * HZ;
+	dpcm->frac_period_rest = dpcm->frac_period_size;
+	dpcm->elapsed = 0;
+	return 0;
+}
+
+static void sdrv_alsa_timer_callback(unsigned long data)
+{
+	struct sdev_alsa_timer_info *dpcm = (struct sdev_alsa_timer_info *)data;
+	int elapsed;
+
+	spin_lock(&dpcm->lock);
+	sdrv_alsa_timer_update(dpcm);
+	sdrv_alsa_timer_rearm(dpcm);
+	elapsed = dpcm->elapsed;
+	dpcm->elapsed = 0;
+	spin_unlock(&dpcm->lock);
+	if (elapsed)
+		snd_pcm_period_elapsed(dpcm->substream);
+}
+
+static snd_pcm_uframes_t sdrv_alsa_timer_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_alsa_timer_info *dpcm = &stream->timer;
+	snd_pcm_uframes_t pos;
+
+	spin_lock(&dpcm->lock);
+	sdrv_alsa_timer_update(dpcm);
+	pos = dpcm->frac_pos / HZ;
+	spin_unlock(&dpcm->lock);
+	return pos;
+}
+
+static int sdrv_alsa_timer_create(struct snd_pcm_substream *substream)
+{
+	struct sdev_pcm_stream_info *stream = sdrv_stream_get(substream);
+	struct sdev_alsa_timer_info *dpcm = &stream->timer;
+
+	spin_lock_init(&dpcm->lock);
+	dpcm->substream = substream;
+	setup_timer(&dpcm->timer, sdrv_alsa_timer_callback,
+		(unsigned long) dpcm);
+	return 0;
+}
+
 static void sdrv_copy_pcm_hw(struct snd_pcm_hardware *dst,
 	struct snd_pcm_hardware *src,
 	struct snd_pcm_hardware *ref_pcm_hw)
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1705337 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromClemens Ladisch <clemens@ladisch.de>
Date2017-08-07 12:40 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubNC2-714-13@gated-at.bofh.it>
In reply to#1705203
Oleksandr Andrushchenko wrote:
> Front sound driver has no real interrupts, so
> playback/capture period passed interrupt needs to be emulated:
> this is done via timer. Add required timer operations,
> this is based on sound/drivers/dummy.c.

A 'real' sound card use the interrupt to synchronize the stream position
between the hardware and the driver.  The hardware triggers an interrupt
immediately after a period has been completely read (for playback) from
the ring buffer by the DMA unit; this tells the driver that it is now
again allowed to write to that part of the buffer.

The dummy driver has no hardware that accesses the buffer, so the period
interrupts are not synchronized to anything.  This is not a suitable
implementation when the samples are actually used.

If you issue interrupts based on the system timer, the position reported
by the .pointer callback and the position where the hardware (backend)
actually accesses the buffer will diverge, which will eventually corrupt
data.

You have to implement period interrupts (and the .pointer callback)
based on when the samples are actually moved from/to the backend.


Regards,
Clemens

[toc] | [prev] | [next] | [standalone]


#1705384 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 13:40 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubOy6-7Bo-21@gated-at.bofh.it>
In reply to#1705337
Hi, Clemens!

On 08/07/2017 01:27 PM, Clemens Ladisch wrote:
> Oleksandr Andrushchenko wrote:
>> Front sound driver has no real interrupts, so
>> playback/capture period passed interrupt needs to be emulated:
>> this is done via timer. Add required timer operations,
>> this is based on sound/drivers/dummy.c.
> A 'real' sound card use the interrupt to synchronize the stream position
> between the hardware and the driver.  The hardware triggers an interrupt
> immediately after a period has been completely read (for playback) from
> the ring buffer by the DMA unit; this tells the driver that it is now
> again allowed to write to that part of the buffer.
Yes, I know that, thank you
> The dummy driver has no hardware that accesses the buffer, so the period
> interrupts are not synchronized to anything.
Exactly
>    This is not a suitable
> implementation when the samples are actually used.

> If you issue interrupts based on the system timer, the position reported
> by the .pointer callback and the position where the hardware (backend)
> actually accesses the buffer will diverge, which will eventually corrupt
> data.
Makes sense, but in my case the buffer from the frontend
is copied into backend's memory, so they don't share the
same buffer as real HW does. But it is still possible that
the new portion of data may arrive and backend will overwrite
the memory which hasn't been played yet because pointers are not
synchronized
> You have to implement period interrupts (and the .pointer callback)
> based on when the samples are actually moved from/to the backend.
Do you think I can implement this in a slightly different way,
without a timer at all, by updating
substream->runtime->hw_ptr_base explicitly in the frontend driver?
Like it was implemented [1], see virtualcard_pcm_pointer
(unfortunately, that driver didn't make it to the kernel).
So, that way, whenever I get an ack/response from the backend that it has
successfully played the buffer I can update hw_ptr_base at the frontend
and thus be always in sync to the backend.
> Regards,
> Clemens
Thank you,
Oleksandr

[1] http://marc.info/?l=xen-devel&m=142185395013970&w=4

[toc] | [prev] | [next] | [standalone]


#1705505 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromClemens Ladisch <clemens@ladisch.de>
Date2017-08-07 15:20 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubQ6S-fz-21@gated-at.bofh.it>
In reply to#1705384
Oleksandr Andrushchenko wrote:
> On 08/07/2017 01:27 PM, Clemens Ladisch wrote:
>> You have to implement period interrupts (and the .pointer callback)
>> based on when the samples are actually moved from/to the backend.
>
> Do you think I can implement this in a slightly different way,
> without a timer at all, by updating
> substream->runtime->hw_ptr_base explicitly in the frontend driver?

As far as I am aware, hw_ptr_base is an internal field that drivers
are not supposed to change.

Just use your own variable, and return it from the .pointer callback.

> So, that way, whenever I get an ack/response from the backend that it has
> successfully played the buffer

That response should come after every period.

How does that interface work?  Is it possible to change the period size,
or at least to detect what it is?


Regards,
Clemens

[toc] | [prev] | [next] | [standalone]


#1705525 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 15:40 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubQqd-nB-7@gated-at.bofh.it>
In reply to#1705505
On 08/07/2017 04:11 PM, Clemens Ladisch wrote:
> Oleksandr Andrushchenko wrote:
>> On 08/07/2017 01:27 PM, Clemens Ladisch wrote:
>>> You have to implement period interrupts (and the .pointer callback)
>>> based on when the samples are actually moved from/to the backend.
>> Do you think I can implement this in a slightly different way,
>> without a timer at all, by updating
>> substream->runtime->hw_ptr_base explicitly in the frontend driver?
> As far as I am aware, hw_ptr_base is an internal field that drivers
> are not supposed to change.
I know that and always considered not a good solution,
this is why I have timer to emulate things
> Just use your own variable, and return it from the .pointer callback.
this can work, but see below
>> So, that way, whenever I get an ack/response from the backend that it has
>> successfully played the buffer
> That response should come after every period.

> How does that interface work?
For the buffer received in .copy_user/.copy_kernel we send
a request to the backend and get response back (async) when it has copied
the bytes into HW/mixer/etc, so the buffer at frontend side can be reused.
So, the amount of bytes in this exchange is not necessarily
a multiply of the period. Also, there is no way to synchronize period
sizes in the front driver and backend to make those equal.
There is also no event from the backend in the
protocol to tell that the period has elapsed, so
sending data in period's size buffers will not probably
help because of possible underruns
>   Is it possible to change the period size,
> or at least to detect what it is?

Unfortunately no, this is not in the protocol.

>
>
> Regards,
> Clemens
you can see the protocol at [1]

Thank you,
Oleksandr

[1] 
https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/tree/include/xen/interface/io/sndif.h?h=for-next

[toc] | [prev] | [next] | [standalone]


#1705543 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromClemens Ladisch <clemens@ladisch.de>
Date2017-08-07 16:00 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubQJz-xP-13@gated-at.bofh.it>
In reply to#1705525
Oleksandr Andrushchenko wrote:
> On 08/07/2017 04:11 PM, Clemens Ladisch wrote:
>> How does that interface work?
>
> For the buffer received in .copy_user/.copy_kernel we send
> a request to the backend and get response back (async) when it has copied
> the bytes into HW/mixer/etc, so the buffer at frontend side can be reused.

So if the frontend sends too many (too large) requests, does the
backend wait until there is enough free space in the buffer before
it does the actual copying and then acks?

If yes, then these acks can be used as interrupts.  (You still
have to count frames, and call snd_pcm_period_elapsed() exactly
when a period boundary was reached or crossed.)

Splitting a large read/write into smaller requests to the backend
would improve the granularity of the known stream position.

The overall latency would be the sum of the sizes of the frontend
and backend buffers.


Why is the protocol designed this way?  Wasn't the goal to expose
some 'real' sound card?


Regards,
Clemens

[toc] | [prev] | [next] | [standalone]


#1705623 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 17:20 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<ubRYZ-1OG-7@gated-at.bofh.it>
In reply to#1705543
On 08/07/2017 04:55 PM, Clemens Ladisch wrote:
> Oleksandr Andrushchenko wrote:
>> On 08/07/2017 04:11 PM, Clemens Ladisch wrote:
>>> How does that interface work?
>> For the buffer received in .copy_user/.copy_kernel we send
>> a request to the backend and get response back (async) when it has copied
>> the bytes into HW/mixer/etc, so the buffer at frontend side can be reused.
> So if the frontend sends too many (too large) requests, does the
> backend wait until there is enough free space in the buffer before
> it does the actual copying and then acks?
Well, the frontend should be backend agnostic,
In our implementation backend is a user-space application which sits
either on top of ALSA driver or PulseAudio: so, it acks correspondingly,
e.g, when, for example, ALSA driver completes .copy_user and returns
from the kernel
> If yes, then these acks can be used as interrupts.
we can probably teach our backend to track periods elapsed for ALSA,
but not sure if it is possible for PulseAudio - do you know if this is also
doable for pulse?

Let's assume backend blocks until the buffer played/consumed...
>    (You still
> have to count frames, and call snd_pcm_period_elapsed() exactly
> when a period boundary was reached or crossed.)
... and what if the buffer has multiple periods? So, that the backend sends
a single response for multiple periods (buffers with fractional period 
number
can be handled separately)?
We will have to either send snd_pcm_period_elapsed once (wrong, because
multiple periods consumed) or multiple times at one time with no delay 
(wrong,
because there will be a confusion that multiple periods were not 
reported for quite
some long time and then there is a burst of events)
Either way the behavior will not be the one desired (please correct me
if I am wrong here)
>
> Splitting a large read/write into smaller requests to the backend
> would improve the granularity of the known stream position.
>
> The overall latency would be the sum of the sizes of the frontend
> and backend buffers.
>
>
> Why is the protocol designed this way?
We also work on para-virtualizing display device and there we tried to use
page flip events from backend to frontend to signal similar to
period interrupt for audio. When multiple displays (read multiple audio 
streams)
were in place we flooded with the system interrupts (which are period 
events in our case)
and performance dropped significantly. This is why we switched to
interrupt emulation, here via timer for audio. The main measures were:
1. Number of events between front and back
2. Latency
With timer approach we reduce 1) to the minimum which is a must (no period
interrupts), but 2) is still here
With emulated period interrupts (protocol events) we have issue with 1)
and still 2) remains.

So, to me, neither approach solves the problem for 100%, so we decided
to stick to timers. Hope, this gives more background on why we did things
the way we did.
>   Wasn't the goal to expose
> some 'real' sound card?
>
yes, but it can be implemented in different ways, please see above
> Regards,
> Clemens
Thank you for your interest,
Oleksandr

[toc] | [prev] | [next] | [standalone]


#1706050 — Re: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-08 08:10 +0200
SubjectRe: [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period interrupt emulation
Message-ID<uc5Sh-3MH-13@gated-at.bofh.it>
In reply to#1705623
On 08/07/2017 06:14 PM, Oleksandr Andrushchenko wrote:
>
> On 08/07/2017 04:55 PM, Clemens Ladisch wrote:
>> Oleksandr Andrushchenko wrote:
>>> On 08/07/2017 04:11 PM, Clemens Ladisch wrote:
>>>> How does that interface work?
>>> For the buffer received in .copy_user/.copy_kernel we send
>>> a request to the backend and get response back (async) when it has 
>>> copied
>>> the bytes into HW/mixer/etc, so the buffer at frontend side can be 
>>> reused.
>> So if the frontend sends too many (too large) requests, does the
>> backend wait until there is enough free space in the buffer before
>> it does the actual copying and then acks?
> Well, the frontend should be backend agnostic,
> In our implementation backend is a user-space application which sits
> either on top of ALSA driver or PulseAudio: so, it acks correspondingly,
> e.g, when, for example, ALSA driver completes .copy_user and returns
> from the kernel
>> If yes, then these acks can be used as interrupts.
> we can probably teach our backend to track periods elapsed for ALSA,
> but not sure if it is possible for PulseAudio - do you know if this is 
> also
> doable for pulse?
>
> Let's assume backend blocks until the buffer played/consumed...
>>    (You still
>> have to count frames, and call snd_pcm_period_elapsed() exactly
>> when a period boundary was reached or crossed.)
> ... and what if the buffer has multiple periods? So, that the backend 
> sends
> a single response for multiple periods (buffers with fractional period 
> number
> can be handled separately)?
> We will have to either send snd_pcm_period_elapsed once (wrong, because
> multiple periods consumed) or multiple times at one time with no delay 
> (wrong,
> because there will be a confusion that multiple periods were not 
> reported for quite
> some long time and then there is a burst of events)
> Either way the behavior will not be the one desired (please correct me
> if I am wrong here)
>>
>> Splitting a large read/write into smaller requests to the backend
>> would improve the granularity of the known stream position.
>>
>> The overall latency would be the sum of the sizes of the frontend
>> and backend buffers.
>>
>>
>> Why is the protocol designed this way?
> We also work on para-virtualizing display device and there we tried to 
> use
> page flip events from backend to frontend to signal similar to
> period interrupt for audio. When multiple displays (read multiple 
> audio streams)
> were in place we flooded with the system interrupts (which are period 
> events in our case)
> and performance dropped significantly. This is why we switched to
> interrupt emulation, here via timer for audio. The main measures were:
> 1. Number of events between front and back
> 2. Latency
> With timer approach we reduce 1) to the minimum which is a must (no 
> period
> interrupts), but 2) is still here
> With emulated period interrupts (protocol events) we have issue with 1)
> and still 2) remains.
>
BTW, there is one more approach to solve this [1],
but it uses its own Xen sound protocol and heavily relies
on Linux implementation, which cannot be a part of a generic protocol
> So, to me, neither approach solves the problem for 100%, so we decided
> to stick to timers. Hope, this gives more background on why we did things
> the way we did.
>>   Wasn't the goal to expose
>> some 'real' sound card?
>>
> yes, but it can be implemented in different ways, please see above
>> Regards,
>> Clemens
> Thank you for your interest,
> Oleksandr

[1] 
https://github.com/OpenXT/pv-linux-drivers/blob/master/archive/openxt-audio/main.c#L356

[toc] | [prev] | [next] | [standalone]


#1705376 — Re: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver

FromTakashi Sakamoto <o-takashi@sakamocchi.jp>
Date2017-08-07 13:40 +0200
SubjectRe: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver
Message-ID<ubOy5-7Bo-5@gated-at.bofh.it>
In reply to#1705191
Hi,

On Aug 7 2017 16:43, Oleksandr Andrushchenko wrote:
> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
> 
> This patch series adds support for Xen [1] para-virtualized
> sound frontend driver. It implements the protocol from
> include/xen/interface/io/sndif.h with the following limitations:
> - mute/unmute is not supported
> - get/set volume is not supported
> Volume control is not supported for the reason that most of the
> use-cases (at the moment) are based on scenarious where
> unprivileged OS (e.g. Android, AGL etc) use software mixers.
> 
> Both capture and playback are supported.
> 
> Thank you,
> Oleksandr
> 
> [1] https://xenproject.org/
> 
> Oleksandr Andrushchenko (11):
>    ALSA: vsnd: Implement driver's probe/remove
>    ALSA: vsnd: Implement Xen bus state handling
>    ALSA: vsnd: Read sound driver configuration from Xen store
>    ALSA: vsnd: Implement Xen event channel handling
>    ALSA: vsnd: Implement handling of shared buffers
>    ALSA: vsnd: Introduce ALSA virtual sound driver
>    ALSA: vsnd: Initialize virtul sound card
>    ALSA: vsnd: Add timer for period interrupt emulation
>    ALSA: vsnd: Implement ALSA PCM operations
>    ALSA: vsnd: Implement communication with backend
>    ALSA: vsnd: Introduce Kconfig option to enable Xen PV sound
> 
>   sound/drivers/Kconfig     |   12 +
>   sound/drivers/Makefile    |    2 +
>   sound/drivers/xen-front.c | 2029 +++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 2043 insertions(+)

As a quick glance of your first patch, it's apparently based on your 
local working branch. I'd like you to post patchset again, which were 
rebased on current upstream[1], so that code reviewers can do their work.

[1] 'for-next' branch in Iwai-san's tree
https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/log/?h=for-next


Regards

Takashi Sakamoto

[toc] | [prev] | [next] | [standalone]


#1705382 — Re: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver

FromOleksandr Andrushchenko <andr2000@gmail.com>
Date2017-08-07 13:40 +0200
SubjectRe: [PATCH 00/11] ALSA: vsnd: Add Xen para-virtualized frontend driver
Message-ID<ubOy5-7Bo-15@gated-at.bofh.it>
In reply to#1705376
Hi,

On 08/07/2017 02:28 PM, Takashi Sakamoto wrote:
> Hi,
>
> On Aug 7 2017 16:43, Oleksandr Andrushchenko wrote:
>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>>
>> This patch series adds support for Xen [1] para-virtualized
>> sound frontend driver. It implements the protocol from
>> include/xen/interface/io/sndif.h with the following limitations:
>> - mute/unmute is not supported
>> - get/set volume is not supported
>> Volume control is not supported for the reason that most of the
>> use-cases (at the moment) are based on scenarious where
>> unprivileged OS (e.g. Android, AGL etc) use software mixers.
>>
>> Both capture and playback are supported.
>>
>> Thank you,
>> Oleksandr
>>
>> [1] https://xenproject.org/
>>
>> Oleksandr Andrushchenko (11):
>>    ALSA: vsnd: Implement driver's probe/remove
>>    ALSA: vsnd: Implement Xen bus state handling
>>    ALSA: vsnd: Read sound driver configuration from Xen store
>>    ALSA: vsnd: Implement Xen event channel handling
>>    ALSA: vsnd: Implement handling of shared buffers
>>    ALSA: vsnd: Introduce ALSA virtual sound driver
>>    ALSA: vsnd: Initialize virtul sound card
>>    ALSA: vsnd: Add timer for period interrupt emulation
>>    ALSA: vsnd: Implement ALSA PCM operations
>>    ALSA: vsnd: Implement communication with backend
>>    ALSA: vsnd: Introduce Kconfig option to enable Xen PV sound
>>
>>   sound/drivers/Kconfig     |   12 +
>>   sound/drivers/Makefile    |    2 +
>>   sound/drivers/xen-front.c | 2029 
>> +++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 2043 insertions(+)
>
> As a quick glance of your first patch, it's apparently based on your 
> local working branch. 
sorry about that, I based the changes on v4.13-rc3 kernel sources
> I'd like you to post patchset again, which were rebased on current 
> upstream[1], so that code reviewers can do their work.
sure, will do that now
>
> [1] 'for-next' branch in Iwai-san's tree
> https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/log/?h=for-next 
>
>
>
> Regards
>
> Takashi Sakamoto

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web