Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1705440 > unrolled thread
| Started by | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| First post | 2017-08-07 14:30 +0200 |
| Last post | 2017-08-10 10:20 +0200 |
| Articles | 9 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 12/12] ALSA: vsnd: Introduce Kconfig option to enable Xen PV sound Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 11/12] ALSA: vsnd: Implement communication with backend Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 10/12] ALSA: vsnd: Implement ALSA PCM operations Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 04/12] ALSA: vsnd: Read sound driver configuration from Xen store Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 07/12] ALSA: vsnd: Introduce ALSA virtual sound driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
[PATCH RESEND1 05/12] ALSA: vsnd: Implement Xen event channel handling Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-07 14:30 +0200
Re: [PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver Takashi Sakamoto <o-takashi@sakamocchi.jp> - 2017-08-10 05:20 +0200
Re: [PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver Oleksandr Andrushchenko <andr2000@gmail.com> - 2017-08-10 10:20 +0200
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver |
| Message-ID | <ubPkt-87B-5@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 Resending because of rebase onto [2] + added missing patch [1] https://xenproject.org/ [2] https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/log/?h=for-next Oleksandr Andrushchenko (12): ALSA: vsnd: Introduce Xen para-virtualized sound frontend driver 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 | 2107 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2121 insertions(+) create mode 100644 sound/drivers/xen-front.c -- 2.7.4
[toc] | [next] | [standalone]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 12/12] ALSA: vsnd: Introduce Kconfig option to enable Xen PV sound |
| Message-ID | <ubPku-87B-27@gated-at.bofh.it> |
| In reply to | #1705440 |
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> Introduce Kconfig option to enable Xen para-virtualized sound frontend driver. Also add sound frontend to the Makefile. Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> --- sound/drivers/Kconfig | 12 ++++++++++++ sound/drivers/Makefile | 2 ++ 2 files changed, 14 insertions(+) diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 7144cc36e8ae..6b8fa6110ca3 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -235,4 +235,16 @@ config SND_AC97_POWER_SAVE_DEFAULT See SND_AC97_POWER_SAVE for more details. +config SND_XEN_FRONTEND + tristate "Xen virtual sound front-end driver" + depends on SND_PCM && XEN + select XEN_XENBUS_FRONTEND + default n + help + This driver implements a front-end for the Xen + para-virtualized sound. + + To compile this driver as a module, choose M here: the module + will be called snd-xen-front. + endif # SND_DRIVERS diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index 1a8440c8b138..70ed920a030f 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -11,6 +11,7 @@ snd-portman2x4-objs := portman2x4.o snd-serial-u16550-objs := serial-u16550.o snd-virmidi-objs := virmidi.o snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o +snd-xen-front-objs := xen-front.o # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-dummy.o @@ -21,5 +22,6 @@ obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o +obj-$(CONFIG_SND_XEN_FRONTEND) += snd-xen-front.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ -- 2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 11/12] ALSA: vsnd: Implement communication with backend |
| Message-ID | <ubPkv-87B-33@gated-at.bofh.it> |
| In reply to | #1705440 |
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]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 10/12] ALSA: vsnd: Implement ALSA PCM operations |
| Message-ID | <ubPkv-87B-35@gated-at.bofh.it> |
| In reply to | #1705440 |
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]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 04/12] ALSA: vsnd: Read sound driver configuration from Xen store |
| Message-ID | <ubPkv-87B-37@gated-at.bofh.it> |
| In reply to | #1705440 |
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Read configuration values from Xen store according
to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different
components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters
Fill in platform data with the configuration read, so
it can be passed to sound driver later.
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
sound/drivers/xen-front.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 530 insertions(+)
diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index c4fd21cac3a7..ef48cbf44cf2 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -20,24 +20,554 @@
#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
#include <xen/platform_pci.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/interface/io/sndif.h>
+/* maximum number of supported streams */
+#define VSND_MAX_STREAM 8
+
+struct cfg_stream {
+ int unique_id;
+ char *xenstore_path;
+ struct snd_pcm_hardware pcm_hw;
+};
+
+struct cfg_pcm_instance {
+ char name[80];
+ int device_id;
+ struct snd_pcm_hardware pcm_hw;
+ int num_streams_pb;
+ struct cfg_stream *streams_pb;
+ int num_streams_cap;
+ struct cfg_stream *streams_cap;
+};
+
+struct cfg_card {
+ char name_short[32];
+ char name_long[80];
+ struct snd_pcm_hardware pcm_hw;
+ int num_pcm_instances;
+ struct cfg_pcm_instance *pcm_instances;
+};
+
+struct sdev_card_plat_data {
+ struct xdrv_info *xdrv_info;
+ struct cfg_card cfg_card;
+};
+
struct xdrv_info {
struct xenbus_device *xb_dev;
spinlock_t io_lock;
struct mutex mutex;
+ struct sdev_card_plat_data cfg_plat_data;
};
+#define MAX_XEN_BUFFER_SIZE (64 * 1024)
+#define MAX_BUFFER_SIZE MAX_XEN_BUFFER_SIZE
+#define MIN_PERIOD_SIZE 64
+#define MAX_PERIOD_SIZE (MAX_BUFFER_SIZE / 8)
+#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \
+ SNDRV_PCM_RATE_8000_48000)
+#define USE_RATE_MIN 5512
+#define USE_RATE_MAX 48000
+#define USE_CHANNELS_MIN 1
+#define USE_CHANNELS_MAX 2
+#define USE_PERIODS_MIN 2
+#define USE_PERIODS_MAX 8
+
+static struct snd_pcm_hardware sdrv_pcm_hw_default = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = USE_PERIODS_MIN,
+ .periods_max = USE_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+struct CFG_HW_SAMPLE_RATE {
+ const char *name;
+ unsigned int mask;
+ unsigned int value;
+};
+
+static struct CFG_HW_SAMPLE_RATE cfg_hw_supported_rates[] = {
+ { .name = "5512", .mask = SNDRV_PCM_RATE_5512, .value = 5512 },
+ { .name = "8000", .mask = SNDRV_PCM_RATE_8000, .value = 8000 },
+ { .name = "11025", .mask = SNDRV_PCM_RATE_11025, .value = 11025 },
+ { .name = "16000", .mask = SNDRV_PCM_RATE_16000, .value = 16000 },
+ { .name = "22050", .mask = SNDRV_PCM_RATE_22050, .value = 22050 },
+ { .name = "32000", .mask = SNDRV_PCM_RATE_32000, .value = 32000 },
+ { .name = "44100", .mask = SNDRV_PCM_RATE_44100, .value = 44100 },
+ { .name = "48000", .mask = SNDRV_PCM_RATE_48000, .value = 48000 },
+ { .name = "64000", .mask = SNDRV_PCM_RATE_64000, .value = 64000 },
+ { .name = "96000", .mask = SNDRV_PCM_RATE_96000, .value = 96000 },
+ { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
+ { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
+};
+
+struct CFG_HW_SAMPLE_FORMAT {
+ const char *name;
+ u64 mask;
+};
+
+static struct CFG_HW_SAMPLE_FORMAT cfg_hw_supported_formats[] = {
+ {
+ .name = XENSND_PCM_FORMAT_U8_STR,
+ .mask = SNDRV_PCM_FMTBIT_U8
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S8_STR,
+ .mask = SNDRV_PCM_FMTBIT_S8
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U16_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U16_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U16_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U16_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S16_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S16_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S16_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S16_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U24_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U24_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U24_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U24_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S24_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S24_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S24_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S24_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U32_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U32_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_U32_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_U32_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S32_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S32_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_S32_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_S32_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_A_LAW_STR,
+ .mask = SNDRV_PCM_FMTBIT_A_LAW
+ },
+ {
+ .name = XENSND_PCM_FORMAT_MU_LAW_STR,
+ .mask = SNDRV_PCM_FMTBIT_MU_LAW
+ },
+ {
+ .name = XENSND_PCM_FORMAT_F32_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_FLOAT_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_F32_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_FLOAT_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_F64_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_F64_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
+ .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
+ .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+ },
+ {
+ .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
+ .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
+ },
+ {
+ .name = XENSND_PCM_FORMAT_MPEG_STR,
+ .mask = SNDRV_PCM_FMTBIT_MPEG
+ },
+ {
+ .name = XENSND_PCM_FORMAT_GSM_STR,
+ .mask = SNDRV_PCM_FMTBIT_GSM
+ },
+};
+
+static void cfg_hw_rates(char *list, unsigned int len,
+ const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+ char *cur_rate;
+ unsigned int cur_mask;
+ unsigned int cur_value;
+ unsigned int rates;
+ unsigned int rate_min;
+ unsigned int rate_max;
+ int i;
+
+ rates = 0;
+ rate_min = -1;
+ rate_max = 0;
+ while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
+ for (i = 0; i < ARRAY_SIZE(cfg_hw_supported_rates); i++)
+ if (!strncasecmp(cur_rate,
+ cfg_hw_supported_rates[i].name,
+ XENSND_SAMPLE_RATE_MAX_LEN)) {
+ cur_mask = cfg_hw_supported_rates[i].mask;
+ cur_value = cfg_hw_supported_rates[i].value;
+ rates |= cur_mask;
+ if (rate_min > cur_value)
+ rate_min = cur_value;
+ if (rate_max < cur_value)
+ rate_max = cur_value;
+ }
+ }
+
+ if (rates) {
+ pcm_hw->rates = rates;
+ pcm_hw->rate_min = rate_min;
+ pcm_hw->rate_max = rate_max;
+ }
+}
+
+static void cfg_formats(char *list, unsigned int len,
+ const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+ u64 formats;
+ char *cur_format;
+ int i;
+
+ formats = 0;
+ while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
+ for (i = 0; i < ARRAY_SIZE(cfg_hw_supported_formats); i++)
+ if (!strncasecmp(cur_format,
+ cfg_hw_supported_formats[i].name,
+ XENSND_SAMPLE_FORMAT_MAX_LEN))
+ formats |= cfg_hw_supported_formats[i].mask;
+ }
+
+ if (formats)
+ pcm_hw->formats = formats;
+}
+
+static void cfg_pcm_hw(const char *path,
+ struct snd_pcm_hardware *parent_pcm_hw,
+ struct snd_pcm_hardware *pcm_hw)
+{
+ char *list;
+ int val;
+ size_t buf_sz;
+ unsigned int len;
+
+ /* inherit parent's PCM HW and read overrides if any */
+ *pcm_hw = *parent_pcm_hw;
+
+ val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
+ if (val)
+ pcm_hw->channels_min = val;
+
+ val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
+ if (val)
+ pcm_hw->channels_max = val;
+
+ list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
+ if (!IS_ERR(list)) {
+ cfg_hw_rates(list, len, path, pcm_hw);
+ kfree(list);
+ }
+
+ list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
+ if (!IS_ERR(list)) {
+ cfg_formats(list, len, path, pcm_hw);
+ kfree(list);
+ }
+
+ buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
+ if (buf_sz)
+ pcm_hw->buffer_bytes_max = buf_sz;
+}
+
+static int cfg_get_stream_type(const char *path, int index,
+ int *num_pb, int *num_cap)
+{
+ char *str = NULL;
+ char *stream_path;
+ int ret;
+
+ *num_pb = 0;
+ *num_cap = 0;
+ stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
+ if (!stream_path) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+ if (IS_ERR(str)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+ sizeof(XENSND_STREAM_TYPE_PLAYBACK)))
+ (*num_pb)++;
+ else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+ sizeof(XENSND_STREAM_TYPE_CAPTURE)))
+ (*num_cap)++;
+ else {
+ ret = -EINVAL;
+ goto fail;
+ }
+ ret = 0;
+
+fail:
+ kfree(stream_path);
+ kfree(str);
+ return ret;
+}
+
+static int cfg_stream(struct xdrv_info *drv_info,
+ struct cfg_pcm_instance *pcm_instance,
+ const char *path, int index, int *cur_pb, int *cur_cap,
+ int *stream_idx)
+{
+ char *str = NULL;
+ char *stream_path;
+ struct cfg_stream *stream;
+ int ret;
+
+ stream_path = devm_kasprintf(&drv_info->xb_dev->dev,
+ GFP_KERNEL, "%s/%d", path, index);
+ if (!stream_path) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+ if (IS_ERR(str)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+ sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+ stream = &pcm_instance->streams_pb[(*cur_pb)++];
+ } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+ sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+ stream = &pcm_instance->streams_cap[(*cur_cap)++];
+ } else {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* get next stream index */
+ stream->unique_id = (*stream_idx)++;
+ stream->xenstore_path = stream_path;
+ /*
+ * check in Xen store if PCM HW configuration exists for this stream
+ * and update if so, e.g. we inherit all values from device's PCM HW,
+ * but can still override some of the values for the stream
+ */
+ cfg_pcm_hw(stream->xenstore_path,
+ &pcm_instance->pcm_hw, &stream->pcm_hw);
+ ret = 0;
+
+fail:
+ kfree(str);
+ return ret;
+}
+
+static int cfg_device(struct xdrv_info *drv_info,
+ struct cfg_pcm_instance *pcm_instance,
+ struct snd_pcm_hardware *parent_pcm_hw,
+ const char *path, int node_index, int *stream_idx)
+{
+ char *str;
+ char *device_path;
+ int ret, i, num_streams;
+ int num_pb, num_cap;
+ int cur_pb, cur_cap;
+ char node[3];
+
+ device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
+ if (!device_path)
+ return -ENOMEM;
+
+ str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
+ if (!IS_ERR(str)) {
+ strncpy(pcm_instance->name, str, sizeof(pcm_instance->name));
+ kfree(str);
+ }
+
+ pcm_instance->device_id = node_index;
+
+ /*
+ * check in Xen store if PCM HW configuration exists for this device
+ * and update if so, e.g. we inherit all values from card's PCM HW,
+ * but can still override some of the values for the device
+ */
+ cfg_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
+
+ /*
+ * find out how many streams were configured in Xen store:
+ * streams must have sequential unique IDs, so stop when one
+ * does not exist
+ */
+ num_streams = 0;
+ do {
+ snprintf(node, sizeof(node), "%d", num_streams);
+ if (!xenbus_exists(XBT_NIL, device_path, node))
+ break;
+
+ num_streams++;
+ } while (num_streams < VSND_MAX_STREAM);
+
+ pcm_instance->num_streams_pb = 0;
+ pcm_instance->num_streams_cap = 0;
+ /* get number of playback and capture streams */
+ for (i = 0; i < num_streams; i++) {
+ ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
+ if (ret < 0)
+ goto fail;
+
+ pcm_instance->num_streams_pb += num_pb;
+ pcm_instance->num_streams_cap += num_cap;
+ }
+
+ if (pcm_instance->num_streams_pb) {
+ pcm_instance->streams_pb = devm_kcalloc(
+ &drv_info->xb_dev->dev,
+ pcm_instance->num_streams_pb,
+ sizeof(struct cfg_stream), GFP_KERNEL);
+ if (!pcm_instance->streams_pb) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (pcm_instance->num_streams_cap) {
+ pcm_instance->streams_cap = devm_kcalloc(
+ &drv_info->xb_dev->dev,
+ pcm_instance->num_streams_cap,
+ sizeof(struct cfg_stream), GFP_KERNEL);
+ if (!pcm_instance->streams_cap) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ cur_pb = 0;
+ cur_cap = 0;
+ for (i = 0; i < num_streams; i++) {
+ ret = cfg_stream(drv_info,
+ pcm_instance, device_path, i, &cur_pb, &cur_cap,
+ stream_idx);
+ if (ret < 0)
+ goto fail;
+ }
+ ret = 0;
+
+fail:
+ kfree(device_path);
+ return ret;
+}
+
+static int cfg_card(struct xdrv_info *drv_info,
+ struct sdev_card_plat_data *plat_data, int *stream_idx)
+{
+ struct xenbus_device *xb_dev = drv_info->xb_dev;
+ int ret, num_devices, i;
+ char node[3];
+
+ num_devices = 0;
+ do {
+ snprintf(node, sizeof(node), "%d", num_devices);
+ if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
+ break;
+
+ num_devices++;
+ } while (num_devices < SNDRV_PCM_DEVICES);
+
+ if (!num_devices) {
+ dev_warn(&xb_dev->dev,
+ "No devices configured for sound card at %s\n",
+ xb_dev->nodename);
+ return -ENODEV;
+ }
+
+ /* start from default PCM HW configuration for the card */
+ cfg_pcm_hw(xb_dev->nodename, &sdrv_pcm_hw_default,
+ &plat_data->cfg_card.pcm_hw);
+
+ plat_data->cfg_card.pcm_instances = devm_kcalloc(
+ &drv_info->xb_dev->dev, num_devices,
+ sizeof(struct cfg_pcm_instance), GFP_KERNEL);
+ if (!plat_data->cfg_card.pcm_instances)
+ return -ENOMEM;
+
+ for (i = 0; i < num_devices; i++) {
+ ret = cfg_device(drv_info,
+ &plat_data->cfg_card.pcm_instances[i],
+ &plat_data->cfg_card.pcm_hw,
+ xb_dev->nodename, i, stream_idx);
+ if (ret < 0)
+ return ret;
+ }
+ plat_data->cfg_card.num_pcm_instances = num_devices;
+ return 0;
+}
+
static void xdrv_remove_internal(struct xdrv_info *drv_info)
{
}
static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
{
+ int stream_idx;
+ int ret;
+
+ drv_info->cfg_plat_data.xdrv_info = drv_info;
+ stream_idx = 0;
+ ret = cfg_card(drv_info, &drv_info->cfg_plat_data, &stream_idx);
+ if (ret < 0)
+ return ret;
return 0;
}
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 07/12] ALSA: vsnd: Introduce ALSA virtual sound driver |
| Message-ID | <ubPkv-87B-39@gated-at.bofh.it> |
| In reply to | #1705440 |
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]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-07 14:30 +0200 |
| Subject | [PATCH RESEND1 05/12] ALSA: vsnd: Implement Xen event channel handling |
| Message-ID | <ubPkv-87B-41@gated-at.bofh.it> |
| In reply to | #1705440 |
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
1. Create event channels for all configured streams and publish
corresponding ring references and event channels in Xen store,
so backend can connect.
2. Implement event channel interrupt handler.
3. Create and destroy event channels with respect to Xen bus state.
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---
sound/drivers/xen-front.c | 269 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 268 insertions(+), 1 deletion(-)
diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index ef48cbf44cf2..a92459b2737e 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -24,14 +24,40 @@
#include <sound/pcm.h>
#include <xen/platform_pci.h>
+#include <xen/events.h>
+#include <xen/grant_table.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/interface/io/sndif.h>
+/*
+ * FIXME: usage of grant reference 0 as invalid grant reference:
+ * grant reference 0 is valid, but never exposed to a PV driver,
+ * because of the fact it is already in use/reserved by the PV console.
+ */
+#define GRANT_INVALID_REF 0
/* maximum number of supported streams */
#define VSND_MAX_STREAM 8
+enum xdrv_evtchnl_state {
+ EVTCHNL_STATE_DISCONNECTED,
+ EVTCHNL_STATE_CONNECTED,
+};
+
+struct xdrv_evtchnl_info {
+ struct xdrv_info *drv_info;
+ struct xen_sndif_front_ring ring;
+ int ring_ref;
+ int port;
+ int irq;
+ struct completion completion;
+ enum xdrv_evtchnl_state state;
+ /* latest response status and its corresponding id */
+ int resp_status;
+ uint16_t resp_id;
+};
+
struct cfg_stream {
int unique_id;
char *xenstore_path;
@@ -65,6 +91,8 @@ struct xdrv_info {
struct xenbus_device *xb_dev;
spinlock_t io_lock;
struct mutex mutex;
+ int num_evt_channels;
+ struct xdrv_evtchnl_info *evt_chnls;
struct sdev_card_plat_data cfg_plat_data;
};
@@ -102,6 +130,244 @@ static struct snd_pcm_hardware sdrv_pcm_hw_default = {
.fifo_size = 0,
};
+static irqreturn_t xdrv_evtchnl_interrupt(int irq, void *dev_id)
+{
+ struct xdrv_evtchnl_info *channel = dev_id;
+ struct xdrv_info *drv_info = channel->drv_info;
+ struct xensnd_resp *resp;
+ RING_IDX i, rp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drv_info->io_lock, flags);
+ if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+ goto out;
+
+again:
+ rp = channel->ring.sring->rsp_prod;
+ /* ensure we see queued responses up to rp */
+ rmb();
+
+ for (i = channel->ring.rsp_cons; i != rp; i++) {
+ resp = RING_GET_RESPONSE(&channel->ring, i);
+ if (resp->id != channel->resp_id)
+ continue;
+ switch (resp->operation) {
+ case XENSND_OP_OPEN:
+ /* fall through */
+ case XENSND_OP_CLOSE:
+ /* fall through */
+ case XENSND_OP_READ:
+ /* fall through */
+ case XENSND_OP_WRITE:
+ channel->resp_status = resp->status;
+ complete(&channel->completion);
+ break;
+
+ default:
+ dev_err(&drv_info->xb_dev->dev,
+ "Operation %d is not supported\n",
+ resp->operation);
+ break;
+ }
+ }
+
+ channel->ring.rsp_cons = i;
+ if (i != channel->ring.req_prod_pvt) {
+ int more_to_do;
+
+ RING_FINAL_CHECK_FOR_RESPONSES(&channel->ring, more_to_do);
+ if (more_to_do)
+ goto again;
+ } else
+ channel->ring.sring->rsp_event = i + 1;
+
+out:
+ spin_unlock_irqrestore(&drv_info->io_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void xdrv_evtchnl_flush(
+ struct xdrv_evtchnl_info *channel)
+{
+ int notify;
+
+ channel->ring.req_prod_pvt++;
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->ring, notify);
+ if (notify)
+ notify_remote_via_irq(channel->irq);
+}
+
+static void xdrv_evtchnl_free(struct xdrv_info *drv_info,
+ struct xdrv_evtchnl_info *channel)
+{
+ if (!channel->ring.sring)
+ return;
+
+ channel->state = EVTCHNL_STATE_DISCONNECTED;
+ channel->resp_status = -EIO;
+ complete_all(&channel->completion);
+
+ if (channel->irq)
+ unbind_from_irqhandler(channel->irq, channel);
+ channel->irq = 0;
+
+ if (channel->port)
+ xenbus_free_evtchn(drv_info->xb_dev, channel->port);
+ channel->port = 0;
+
+ if (channel->ring_ref != GRANT_INVALID_REF)
+ gnttab_end_foreign_access(channel->ring_ref, 0,
+ (unsigned long)channel->ring.sring);
+ channel->ring_ref = GRANT_INVALID_REF;
+ channel->ring.sring = NULL;
+}
+
+static void xdrv_evtchnl_free_all(struct xdrv_info *drv_info)
+{
+ int i;
+
+ if (!drv_info->evt_chnls)
+ return;
+
+ for (i = 0; i < drv_info->num_evt_channels; i++)
+ xdrv_evtchnl_free(drv_info, &drv_info->evt_chnls[i]);
+
+ devm_kfree(&drv_info->xb_dev->dev, drv_info->evt_chnls);
+ drv_info->evt_chnls = NULL;
+}
+
+static int xdrv_evtchnl_alloc(struct xdrv_info *drv_info,
+ struct xdrv_evtchnl_info *evt_channel)
+{
+ struct xenbus_device *xb_dev = drv_info->xb_dev;
+ struct xen_sndif_sring *sring;
+ grant_ref_t gref;
+ int ret;
+
+ evt_channel->drv_info = drv_info;
+ init_completion(&evt_channel->completion);
+ evt_channel->state = EVTCHNL_STATE_DISCONNECTED;
+ evt_channel->ring_ref = GRANT_INVALID_REF;
+ evt_channel->ring.sring = NULL;
+ evt_channel->port = 0;
+ evt_channel->irq = 0;
+
+ sring = (struct xen_sndif_sring *)get_zeroed_page(
+ GFP_NOIO | __GFP_HIGH);
+ if (!sring) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ SHARED_RING_INIT(sring);
+ FRONT_RING_INIT(&evt_channel->ring, sring, XEN_PAGE_SIZE);
+ ret = xenbus_grant_ring(xb_dev, sring, 1, &gref);
+ if (ret < 0)
+ goto fail;
+ evt_channel->ring_ref = gref;
+
+ ret = xenbus_alloc_evtchn(xb_dev, &evt_channel->port);
+ if (ret < 0)
+ goto fail;
+
+ ret = bind_evtchn_to_irqhandler(evt_channel->port,
+ xdrv_evtchnl_interrupt, 0, xb_dev->devicetype, evt_channel);
+ if (ret < 0)
+ goto fail;
+
+ evt_channel->irq = ret;
+ return 0;
+
+fail:
+ dev_err(&xb_dev->dev, "Failed to allocate ring: %d\n", ret);
+ return ret;
+}
+
+static int xdrv_evtchnl_create(struct xdrv_info *drv_info,
+ struct xdrv_evtchnl_info *evt_channel,
+ const char *path)
+{
+ int ret;
+
+ ret = xdrv_evtchnl_alloc(drv_info, evt_channel);
+ if (ret < 0) {
+ dev_err(&drv_info->xb_dev->dev,
+ "allocating event channel: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * write values to Xen store, so backend can find ring reference
+ * and event channel
+ */
+ ret = xenbus_printf(XBT_NIL, path, XENSND_FIELD_RING_REF, "%u",
+ evt_channel->ring_ref);
+ if (ret < 0) {
+ dev_err(&drv_info->xb_dev->dev,
+ "writing " XENSND_FIELD_RING_REF": %d\n", ret);
+ return ret;
+ }
+
+ ret = xenbus_printf(XBT_NIL, path, XENSND_FIELD_EVT_CHNL, "%u",
+ evt_channel->port);
+ if (ret < 0) {
+ dev_err(&drv_info->xb_dev->dev,
+ "writing " XENSND_FIELD_EVT_CHNL": %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int xdrv_evtchnl_create_all(struct xdrv_info *drv_info,
+ int num_streams)
+{
+ struct cfg_card *cfg_card;
+ int d, ret = 0;
+
+ drv_info->evt_chnls = devm_kcalloc(&drv_info->xb_dev->dev,
+ num_streams, sizeof(struct xdrv_evtchnl_info), GFP_KERNEL);
+ if (!drv_info->evt_chnls) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ cfg_card = &drv_info->cfg_plat_data.cfg_card;
+ /* iterate over devices and their streams and create event channels */
+ for (d = 0; d < cfg_card->num_pcm_instances; d++) {
+ struct cfg_pcm_instance *pcm_instance;
+ int s, stream_idx;
+
+ pcm_instance = &cfg_card->pcm_instances[d];
+
+ for (s = 0; s < pcm_instance->num_streams_pb; s++) {
+ stream_idx = pcm_instance->streams_pb[s].unique_id;
+ ret = xdrv_evtchnl_create(drv_info,
+ &drv_info->evt_chnls[stream_idx],
+ pcm_instance->streams_pb[s].xenstore_path);
+ if (ret < 0)
+ goto fail;
+ }
+
+ for (s = 0; s < pcm_instance->num_streams_cap; s++) {
+ stream_idx = pcm_instance->streams_cap[s].unique_id;
+ ret = xdrv_evtchnl_create(drv_info,
+ &drv_info->evt_chnls[stream_idx],
+ pcm_instance->streams_cap[s].xenstore_path);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+ if (ret < 0)
+ goto fail;
+
+ drv_info->num_evt_channels = num_streams;
+ return 0;
+
+fail:
+ xdrv_evtchnl_free_all(drv_info);
+ return ret;
+}
+
struct CFG_HW_SAMPLE_RATE {
const char *name;
unsigned int mask;
@@ -556,6 +822,7 @@ static int cfg_card(struct xdrv_info *drv_info,
static void xdrv_remove_internal(struct xdrv_info *drv_info)
{
+ xdrv_evtchnl_free_all(drv_info);
}
static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
@@ -568,7 +835,7 @@ static int xdrv_be_on_initwait(struct xdrv_info *drv_info)
ret = cfg_card(drv_info, &drv_info->cfg_plat_data, &stream_idx);
if (ret < 0)
return ret;
- return 0;
+ return xdrv_evtchnl_create_all(drv_info, stream_idx);
}
static inline int xdrv_be_on_connected(struct xdrv_info *drv_info)
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Takashi Sakamoto <o-takashi@sakamocchi.jp> |
|---|---|
| Date | 2017-08-10 05:20 +0200 |
| Subject | Re: [PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver |
| Message-ID | <ucMaR-8li-3@gated-at.bofh.it> |
| In reply to | #1705440 |
Hi,
On Aug 7 2017 21:22, 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
>
> Resending because of rebase onto [2] + added missing patch
>
> [1] https://xenproject.org/
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/log/?h=for-next
>
> Oleksandr Andrushchenko (12):
> ALSA: vsnd: Introduce Xen para-virtualized sound frontend driver
> 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 | 2107 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 2121 insertions(+)
> create mode 100644 sound/drivers/xen-front.c
For this patchset, I have the same concern which Clemens Ladisch
denoted[1]. If I can understand your explanation about queueing between
Dom0/DomU stuffs, the concern can be described in short words; this
driver works without any synchronization to data transmission by actual
sound hardwares.
In design of ALSA PCM core, drivers are expected to synchronize to
actual hardwares for semi-realtime data transmission. The
synchronization is done by two points:
1) Interrupts to respond events from actual hardwares.
2) Positions of actual data transmission in any serial sound interfaces
of actual hardwares.
These two points comes from typical designs of actual hardwares, thus
they doesn't come from unfair, unreasonable, intrusive demands from
software side.
In design of typical stuffs on para-virtualization, Dom0 stuffs are hard
to give enough abstraction of sound hardwares in these two points for
DomU stuffs. Especially, it cannot abstract point 2) at all because the
value of position should be accurate against actual time frame, while
there's an overhead for DomU stuffs to read it. When DomU stuffs handles
the value, the value is enough past due to context switches between
Dom0/DomU. Therefore, this driver must rely on point 1) to synchronize
to actual sound hardwares. Typically, drivers configure hardwares to
generate interrupts per period of PCM buffer. This means that this
driver should notify to Dom0 about the value of period size requested
by applications.
In 'include/xen/interface/io/sndif.h', there's no functionalities I
described the above:
1. notifications from DomU to Dom0 about the size of period for
interrupts from actual hardwares. Or no way from Dom0 to DomU about
the configured size of the period.
2. notifications of the interrupts from actual hardwares to DomU.
For the reasons, your driver used kernel's timer interface to generate
'pseudo' interrupts for the purpose. However, it depends on Dom0's
abstraction different from sound hardwares and Linux kernel's
abstraction for timer functionality. In this case, gap between 'actual'
interrupts from hardware and the 'pseudo' interrupts from a combination
of several components brings unexpected result on several situations.
I think this is defects of 'sndif' interface in Xen side. I think it
better for you to work in Xen community to improve the above interface
at first, then work for Linux stuffs.
Additionally, in next time, please remind of several points below:
* When a first patch adds an initial code for drivers, it should
include entries for Makefile and Kconfig, so that the driver can be
built even if it's still in an initial shape. Each patch should be
self-contained and should be in a shape so that developers easily run
bisecting. In other words, your first patch[2] includes modification
for Makefile and Kconfig in your last patch[3].
* When any read-only symbols is added, it should have 'const'
qualifier so that the symbol places to .rodata section of ELF
binaries. For example, in your code, 'alsa_sndif_formats' is such an
symbol. In recent Linux development, some developers work for
constifying such symbols. Please remind of their continuous works in
upstream[4].
* You can split your driver to several files. In
'include/xen/interface/io/sndif.h', Dom0 produces functionalities for
DomU to control gain/volume/mute and in future your driver may get
more codes. If split to several files make it readable, it should be
done.
* In my taste, a prefix of the subject line should be 'xen-front',
instead of 'vsnd'. It comes from name of your driver.
[1] [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period
interrupt emulation
http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123617.html
[2] [PATCH RESEND1 01/12] ALSA: vsnd: Introduce Xen para-virtualized
sound frontend driver
http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123654.html
[3] [alsa-devel] [PATCH RESEND1 12/12] ALSA: vsnd: Introduce Kconfig
option to enable Xen PV sound
http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123662.html
[4] You can see many posts for this; e.g. [alsa-devel] [PATCH 0/7]
constify ALSA usb_device_id.
http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123564.html
Regards
Takashi Sakamoto
[toc] | [prev] | [next] | [standalone]
| From | Oleksandr Andrushchenko <andr2000@gmail.com> |
|---|---|
| Date | 2017-08-10 10:20 +0200 |
| Subject | Re: [PATCH RESEND1 00/12] ALSA: vsnd: Add Xen para-virtualized frontend driver |
| Message-ID | <ucQRc-2OS-13@gated-at.bofh.it> |
| In reply to | #1708184 |
Hi, thank you very much for valuable comments and your time! On 08/10/2017 06:14 AM, Takashi Sakamoto wrote: > Hi, > > On Aug 7 2017 21:22, 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 >> >> Resending because of rebase onto [2] + added missing patch >> >> [1] https://xenproject.org/ >> [2] >> https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/log/?h=for-next >> >> Oleksandr Andrushchenko (12): >> ALSA: vsnd: Introduce Xen para-virtualized sound frontend driver >> 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 | 2107 >> +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 2121 insertions(+) >> create mode 100644 sound/drivers/xen-front.c > > For this patchset, I have the same concern which Clemens Ladisch > denoted[1]. If I can understand your explanation about queueing between > Dom0/DomU stuffs, the concern can be described in short words; this > driver works without any synchronization to data transmission by actual > sound hardwares. > Yes, both your concerns and understanding are correct > In design of ALSA PCM core, drivers are expected to synchronize to > actual hardwares for semi-realtime data transmission. The > synchronization is done by two points: > 1) Interrupts to respond events from actual hardwares. > 2) Positions of actual data transmission in any serial sound interfaces > of actual hardwares. > > These two points comes from typical designs of actual hardwares, thus > they doesn't come from unfair, unreasonable, intrusive demands from > software side. > This clear, thank you > In design of typical stuffs on para-virtualization, Dom0 stuffs are hard > to give enough abstraction of sound hardwares in these two points for > DomU stuffs. Especially, it cannot abstract point 2) at all because the > value of position should be accurate against actual time frame, while > there's an overhead for DomU stuffs to read it. When DomU stuffs handles > the value, the value is enough past due to context switches between > Dom0/DomU. Therefore, this driver must rely on point 1) to synchronize > to actual sound hardwares. Which will also introduce some latency too: time needed to deliver and handle interrupt from Dom0 to DomU > Typically, drivers configure hardwares to > generate interrupts per period of PCM buffer. This means that this > driver should notify to Dom0 about the value of period size requested > by applications. > > In 'include/xen/interface/io/sndif.h', there's no functionalities I > described the above: > 1. notifications from DomU to Dom0 about the size of period for > interrupts from actual hardwares. Or no way from Dom0 to DomU about > the configured size of the period. Ok, then on "open" command I will pass from DomU to Dom0 an additional parameter, period size. Then Dom0 will respond with actual period size for DomU to use. So, this way period size will be negotiated. Does the above look ok to you? > 2. notifications of the interrupts from actual hardwares to DomU. > Ok, I will introduce an event from Dom0 to DomU to signal period elapsed. Taking into account the fact that period size may be as small as, say, 1ms, do you think we can/need to mangle period size in 1) on Dom0 side to be reasonable, so we do not flood with interrupts/events from Dom0 to DomU? Do you see any "formula" to determine that reasonable/acceptable period limit, so both Dom0 and DomU are happy? > For the reasons, your driver used kernel's timer interface to generate > 'pseudo' interrupts for the purpose. However, it depends on Dom0's > abstraction different from sound hardwares and Linux kernel's > abstraction for timer functionality. In this case, gap between 'actual' > interrupts from hardware and the 'pseudo' interrupts from a combination > of several components brings unexpected result on several situations. > You are right > I think this is defects of 'sndif' interface in Xen side. I think it > better for you to work in Xen community to improve the above interface > at first, then work for Linux stuffs. > Please see above for planned changes to the protocol > > Additionally, in next time, please remind of several points below: > * When a first patch adds an initial code for drivers, it should > include entries for Makefile and Kconfig, so that the driver can be > built even if it's still in an initial shape. Will do > Each patch should be > self-contained and should be in a shape so that developers easily run > bisecting. In other words, your first patch[2] includes modification > for Makefile and Kconfig in your last patch[3]. Will do > * When any read-only symbols is added, it should have 'const' > qualifier so that the symbol places to .rodata section of ELF > binaries. For example, in your code, 'alsa_sndif_formats' is such an > symbol. In recent Linux development, some developers work for > constifying such symbols. Please remind of their continuous works in > upstream[4]. Will do > * You can split your driver to several files. In > 'include/xen/interface/io/sndif.h', Dom0 produces functionalities for > DomU to control gain/volume/mute and in future your driver may get > more codes. If split to several files make it readable, it should be > done. Will do. If I split, do you think it would be better to move the driver from sound/drivers to sound/xen folder, so all those files do not mix with the rest? > * In my taste, a prefix of the subject line should be 'xen-front', > instead of 'vsnd'. It comes from name of your driver. > Will do > [1] [alsa-devel] [PATCH 08/11] ALSA: vsnd: Add timer for period > interrupt emulation > http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123617.html > > [2] [PATCH RESEND1 01/12] ALSA: vsnd: Introduce Xen para-virtualized > sound frontend driver > http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123654.html > > [3] [alsa-devel] [PATCH RESEND1 12/12] ALSA: vsnd: Introduce Kconfig > option to enable Xen PV sound > http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123662.html > > [4] You can see many posts for this; e.g. [alsa-devel] [PATCH 0/7] > constify ALSA usb_device_id. > http://mailman.alsa-project.org/pipermail/alsa-devel/2017-August/123564.html > > > Regards > > Takashi Sakamoto Thank you, Oleksandr
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web