* [PATCH v2 0/2] Add sound frontend driver
@ 2015-01-20 9:03 Oleksandr Dmytryshyn
2015-01-20 9:03 ` [PATCH v2 1/2] xen-sndfront: add " Oleksandr Dmytryshyn
2015-01-20 9:04 ` [PATCH v2 2/2] xen-sndfront: add capture support Oleksandr Dmytryshyn
0 siblings, 2 replies; 5+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-20 9:03 UTC (permalink / raw)
To: xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela
Hi to all.
Next series of patches introduces Para-virtual sound driver
CONFIG_XEN_SND_FRONTEND should be selected to use this driver.
Frontend driver registers an virtual sound card and sends/receives
an PCM streams to/from the backend driver. Backend driver is an
user-space application which uses ALSA with dmix plugin to play/capture audio.
Changes since v1:
* 2 first patches are squashed
* ALSA specific structure is renamed
* used fixed width types in sndif.h
Iurii Konovalenko (1):
xen-sndfront: add capture support
Oleksandr Dmytryshyn (1):
xen-sndfront: add sound frontend driver
include/xen/interface/io/sndif.h | 208 +++++
sound/drivers/Kconfig | 10 +
sound/drivers/Makefile | 2 +
sound/drivers/xen-sndfront.c | 1613 ++++++++++++++++++++++++++++++++++++++
4 files changed, 1833 insertions(+)
create mode 100644 include/xen/interface/io/sndif.h
create mode 100644 sound/drivers/xen-sndfront.c
--
1.9.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] xen-sndfront: add sound frontend driver
2015-01-20 9:03 [PATCH v2 0/2] Add sound frontend driver Oleksandr Dmytryshyn
@ 2015-01-20 9:03 ` Oleksandr Dmytryshyn
2015-01-21 15:39 ` Jaroslav Kysela
2015-01-20 9:04 ` [PATCH v2 2/2] xen-sndfront: add capture support Oleksandr Dmytryshyn
1 sibling, 1 reply; 5+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-20 9:03 UTC (permalink / raw)
To: xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela
This is Para-virtual sound driver.
Frontend driver registers an virtual sound card
and sends an PCM streams to the backend driver.
Backend driver is an user-space application and
uses ALSA with dmix plugin to play audio.
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
Signed-off-by: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
---
include/xen/interface/io/sndif.h | 208 ++++++
sound/drivers/Kconfig | 10 +
sound/drivers/Makefile | 2 +
sound/drivers/xen-sndfront.c | 1478 ++++++++++++++++++++++++++++++++++++++
4 files changed, 1698 insertions(+)
create mode 100644 include/xen/interface/io/sndif.h
create mode 100644 sound/drivers/xen-sndfront.c
diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h
new file mode 100644
index 0000000..38a5f1e
--- /dev/null
+++ b/include/xen/interface/io/sndif.h
@@ -0,0 +1,208 @@
+/******************************************************************************
+ * sndif.h
+ *
+ * Unified sound-device I/O interface for Xen guest OSes.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (C) 2013-2015 GlobalLogic Inc.
+ */
+
+#ifndef __XEN_PUBLIC_IO_SNDIF_H__
+#define __XEN_PUBLIC_IO_SNDIF_H__
+
+#include <xen/interface/io/ring.h>
+#include <xen/interface/grant_table.h>
+
+/*
+ * Feature and Parameter Negotiation
+ * =================================
+ * The two halves of a Xen vsnd driver utilize nodes within the XenStore to
+ * communicate capabilities and to negotiate operating parameters. This
+ * section enumerates these nodes which reside in the respective front and
+ * backend portions of the XenStore, following the XenBus convention.
+ *
+ * All data in the XenStore is stored as strings. Nodes specifying numeric
+ * values are encoded in decimal. Integer value ranges listed below are
+ * expressed as fixed sized integer types capable of storing the conversion
+ * of a properly formated node string, without loss of information.
+ *
+ * Any specified default value is in effect if the corresponding XenBus node
+ * is not present in the XenStore.
+ *
+ * XenStore nodes in sections marked "PRIVATE" are solely for use by the
+ * driver side whose XenBus tree contains them.
+ *
+ *****************************************************************************
+ * Backend XenBus Nodes
+ *****************************************************************************
+ *
+ *------------------ Backend Device Identification (PRIVATE) ------------------
+ *
+ * stream_id
+ * Values: <uint32_t>
+ *
+ * Virtuelized stream number
+ *
+ *****************************************************************************
+ * Frontend XenBus Nodes
+ *****************************************************************************
+ *
+ *----------------------- Request Transport Parameters -----------------------
+ *
+ * event-channel
+ * Values: <uint32_t>
+ *
+ * The identifier of the Xen event channel used to signal activity
+ * in the ring buffer.
+ *
+ * ring-ref
+ * Values: <uint32_t>
+ * Notes: 6
+ *
+ * The Xen grant reference granting permission for the backend to map
+ * the sole page in a single page sized ring buffer.
+ */
+
+/*
+ * PCM FORMATS.
+ */
+#define SNDIF_PCM_FORMAT_S8 (0)
+#define SNDIF_PCM_FORMAT_U8 (1)
+#define SNDIF_PCM_FORMAT_S16_LE (2)
+#define SNDIF_PCM_FORMAT_S16_BE (3)
+#define SNDIF_PCM_FORMAT_U16_LE (4)
+#define SNDIF_PCM_FORMAT_U16_BE (5)
+
+/* low three bytes */
+#define SNDIF_PCM_FORMAT_S24_LE (6)
+
+/* low three bytes */
+#define SNDIF_PCM_FORMAT_S24_BE (7)
+
+/* low three bytes */
+#define SNDIF_PCM_FORMAT_U24_LE (8)
+
+/* low three bytes */
+#define SNDIF_PCM_FORMAT_U24_BE (9)
+
+#define SNDIF_PCM_FORMAT_S32_LE (10)
+#define SNDIF_PCM_FORMAT_S32_BE (11)
+#define SNDIF_PCM_FORMAT_U32_LE (12)
+#define SNDIF_PCM_FORMAT_U32_BE (13)
+
+/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+#define SNDIF_PCM_FORMAT_FLOAT_LE (14)
+
+/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+#define SNDIF_PCM_FORMAT_FLOAT_BE (15)
+
+/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+#define SNDIF_PCM_FORMAT_FLOAT64_LE (16)
+
+/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+#define SNDIF_PCM_FORMAT_FLOAT64_BE (17)
+
+/* IEC-958 subframe, Little Endian */
+#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_LE (18)
+
+/* IEC-958 subframe, Big Endian */
+#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_BE (19)
+
+#define SNDIF_PCM_FORMAT_MU_LAW (20)
+#define SNDIF_PCM_FORMAT_A_LAW (21)
+#define SNDIF_PCM_FORMAT_IMA_ADPCM (22)
+#define SNDIF_PCM_FORMAT_MPEG (23)
+#define SNDIF_PCM_FORMAT_GSM (24)
+#define SNDIF_PCM_FORMAT_SPECIAL (31)
+
+/*
+ * REQUEST CODES.
+ */
+#define SNDIF_OP_OPEN 0
+#define SNDIF_OP_CLOSE 1
+#define SNDIF_OP_READ 2
+#define SNDIF_OP_WRITE 3
+#define SNDIF_SET_VOLUME 4
+#define SNDIF_GET_VOLUME 5
+
+#define SNDIF_MAX_PAGES_PER_REQUEST 10
+
+/*
+ * STATUS RETURN CODES.
+ */
+ /* Operation failed for some unspecified reason (-EIO). */
+#define SNDIF_RSP_ERROR -1
+ /* Operation completed successfully. */
+#define SNDIF_RSP_OKAY 0
+
+struct snd_params {
+ uint32_t format; /* SNDIF_PCM_FORMAT_??? */
+ uint32_t channels; /* Channels count */
+ uint32_t rate; /* Data rate */
+};
+
+struct sndif_request_common {
+ uint64_t id; /* private guest value, echoed in resp */
+ struct snd_params _pad1;
+ uint32_t _pad2;
+ uint32_t _pad3;
+} __attribute__((__packed__));
+
+struct sndif_request_open {
+ uint64_t id; /* private guest value, echoed in resp */
+ struct snd_params snd_params;
+ uint32_t stream;
+ uint32_t _pad2;
+} __attribute__((__packed__));
+
+struct sndif_request_rw {
+ uint64_t id; /* private guest value, echoed in resp */
+ struct snd_params _pad1;
+ uint32_t len;
+ uint32_t _pad2;
+ grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
+} __attribute__((__packed__));
+
+struct sndif_request_volume {
+ uint64_t id; /* private guest value, echoed in resp */
+ struct snd_params _pad1;
+ uint32_t left;
+ uint32_t right;
+} __attribute__((__packed__));
+
+struct sndif_request {
+ uint8_t operation; /* SNDIF_OP_??? */
+ union {
+ struct sndif_request_common common;
+ struct sndif_request_open open;
+ struct sndif_request_rw rw;
+ struct sndif_request_volume vol;
+ } u;
+} __attribute__((__packed__));
+
+struct sndif_response {
+ uint64_t id; /* copied from request */
+ uint8_t operation; /* copied from request */
+ int16_t status; /* SNDIF_RSP_??? */
+};
+
+DEFINE_RING_TYPES(sndif, struct sndif_request, struct sndif_response);
+
+#endif /* __XEN_PUBLIC_IO_SNDIF_H__ */
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 8545da9..cd3db5a 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -24,6 +24,16 @@ config SND_AC97_CODEC
select AC97_BUS
select SND_VMASTER
+config XEN_SND_FRONTEND
+ tristate "Xen virtual audio front-end driver support"
+ depends on SND && XEN_DOMU
+ default n
+ select SND_PCM
+ help
+ This driver implements the front-end of the Xen virtual
+ audio driver. It communicates with a back-end
+ in another domain.
+
menuconfig SND_DRIVERS
bool "Generic sound devices"
default y
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 1a8440c..f9f7e19 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
+xen-sndfrontend-objs := xen-sndfront.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_XEN_SND_FRONTEND) += xen-sndfrontend.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
new file mode 100644
index 0000000..d0367c2
--- /dev/null
+++ b/sound/drivers/xen-sndfront.c
@@ -0,0 +1,1478 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Copyright (C) 2013-2015 GlobalLogic Inc.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/hrtimer.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+
+#include <linux/uaccess.h>
+
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/xenbus.h>
+#include <xen/interface/grant_table.h>
+
+#include <xen/interface/io/protocols.h>
+#include <xen/interface/io/sndif.h>
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}");
+
+#define VSND_WAIT_ANSWER_TOUT 5000
+
+#define MAX_PCM_DEVICES 1
+#define MAX_PCM_SUBSTREAMS 1
+#define MAX_BUFFER_SIZE (64*1024)
+
+/* defaults */
+#define MIN_PERIOD_SIZE 64
+#define MAX_PERIOD_SIZE (4*1024)
+#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 5500
+#define USE_RATE_MAX 48000
+#define USE_CHANNELS_MIN 1
+#define USE_CHANNELS_MAX 2
+#define USE_PERIODS_MIN 1
+#define USE_PERIODS_MAX 1024
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for virtual soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for virtual soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this virtual soundcard.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Soundcard model.");
+
+#define MIXER_ADDR_MASTER_IN 0
+#define MIXER_ADDR_MASTER_OUT 1
+#define MIXER_ADDR_LAST MIXER_ADDR_MASTER_OUT
+
+struct vsnd_card {
+ struct sndfront_info *fr_info;
+ unsigned int stream_id;
+ grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST];
+ unsigned char *buf;
+};
+
+#define SND_RING_SIZE __CONST_RING_SIZE(sndif, PAGE_SIZE)
+
+enum sndif_state {
+ SNDIF_STATE_DISCONNECTED,
+ SNDIF_STATE_CONNECTED,
+ SNDIF_STATE_SUSPENDED,
+};
+
+struct sndfront_info {
+ struct mutex mutex; /* protect sndfront closing state */
+ struct completion completion;
+ spinlock_t io_lock; /* protect 'connected' member */
+ struct xenbus_device *xbdev;
+ enum sndif_state connected;
+ int ring_ref;
+ struct sndif_front_ring ring;
+ unsigned int evtchn, irq;
+ struct vsnd_card *vcard;
+ int bret_code;
+ struct platform_device *card_dev;
+};
+
+#define GRANT_INVALID_REF 0
+
+struct virtualcard_model {
+ const char *name;
+ u64 formats;
+ size_t buffer_bytes_max;
+ size_t period_bytes_min;
+ size_t period_bytes_max;
+ unsigned int periods_min;
+ unsigned int periods_max;
+ unsigned int rates;
+ unsigned int rate_min;
+ unsigned int rate_max;
+ unsigned int channels_min;
+ unsigned int channels_max;
+};
+
+struct stream_info {
+ snd_pcm_uframes_t position; /* Current position */
+ snd_pcm_uframes_t crossed; /* Number of crossed writes*/
+ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */
+ unsigned int rate; /* rate in Hz */
+ unsigned int channels; /* channels */
+ bool opened; /* opened status */
+};
+
+struct snd_virtualcard {
+ struct snd_card *card;
+ struct virtualcard_model *model;
+ struct snd_pcm *pcm;
+ struct snd_pcm_hardware pcm_hw;
+ spinlock_t mixer_lock; /* protect mixer settings */
+ int mixer_volume[MIXER_ADDR_LAST+1][2];
+ int capture_source[MIXER_ADDR_LAST+1][2];
+ struct sndfront_info *fr_info;
+ struct stream_info streams[2];
+};
+
+/*
+ * card models
+ */
+
+struct virtualcard_model model_ac97 = {
+ .name = "ac97",
+ .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+};
+
+struct virtualcard_model *virtualcard_models[] = {
+ &model_ac97,
+ NULL
+};
+
+/*
+ * PCM interface
+ */
+
+static struct snd_pcm_hardware virtualcard_pcm_hardware = {
+ .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,
+};
+
+static inline
+struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard,
+ struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &virtualcard->streams[0];
+ else
+ return &virtualcard->streams[1];
+}
+
+static unsigned long vmalloc_to_mfn(void *address)
+{
+ return pfn_to_mfn(vmalloc_to_pfn(address));
+}
+
+static inline void flush_requests(struct sndfront_info *info)
+{
+ int notify;
+
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify);
+
+ if (notify)
+ notify_remote_via_irq(info->irq);
+}
+
+static int sndif_queue_request_open(struct sndfront_info *info,
+ snd_pcm_format_t format,
+ unsigned int channels,
+ unsigned int rate)
+{
+ struct sndif_request *req;
+
+ if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
+ return 1;
+
+ req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
+
+ req->operation = SNDIF_OP_OPEN;
+ req->u.open.id = info->vcard->stream_id;
+ req->u.open.snd_params.format = format;
+ req->u.open.snd_params.channels = channels;
+ req->u.open.snd_params.rate = rate;
+ info->ring.req_prod_pvt++;
+
+ flush_requests(info);
+ return 0;
+}
+
+static int sndif_queue_request_close(struct sndfront_info *info)
+{
+ struct sndif_request *req;
+
+ if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
+ return 1;
+
+ req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
+
+ req->operation = SNDIF_OP_CLOSE;
+ req->u.open.id = info->vcard->stream_id;
+
+ info->ring.req_prod_pvt++;
+
+ flush_requests(info);
+ return 0;
+}
+
+static int sndif_queue_request_write(struct sndfront_info *info,
+ unsigned int len)
+{
+ struct sndif_request *req;
+ grant_ref_t *gref;
+ int i;
+
+ if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
+ return 1;
+
+ req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
+
+ req->operation = SNDIF_OP_WRITE;
+ req->u.rw.id = info->vcard->stream_id;
+
+ req->u.rw.len = len;
+
+ gref = info->vcard->grefs;
+
+ for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+ req->u.rw.gref[i] = gref[i];
+
+ info->ring.req_prod_pvt++;
+
+ flush_requests(info);
+ return 0;
+}
+
+static int alsa_pcm_open(struct sndfront_info *info,
+ snd_pcm_format_t format,
+ unsigned int channels,
+ unsigned int rate)
+{
+ unsigned long answer_tout;
+
+ reinit_completion(&info->completion);
+
+ if (sndif_queue_request_open(info, format, channels, rate))
+ return -EIO;
+
+ answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+ if (wait_for_completion_interruptible_timeout(&info->completion,
+ answer_tout) <= 0)
+ return -ETIMEDOUT;
+
+ return info->bret_code;
+}
+
+static int alsa_pcm_close(struct sndfront_info *info)
+{
+ unsigned long answer_tout;
+
+ reinit_completion(&info->completion);
+
+ if (sndif_queue_request_close(info))
+ return -EIO;
+
+ answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+ if (wait_for_completion_interruptible_timeout(&info->completion,
+ answer_tout) <= 0)
+ return -ETIMEDOUT;
+
+ return info->bret_code;
+}
+
+static int alsa_pcm_write(struct sndfront_info *info, char __user *buf,
+ int len)
+{
+ unsigned char *shared_data;
+ unsigned long answer_tout;
+
+ shared_data = info->vcard->buf;
+
+ if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+ return -EFAULT;
+
+ if (copy_from_user(shared_data, buf, len))
+ return -EFAULT;
+
+ reinit_completion(&info->completion);
+
+ if (sndif_queue_request_write(info, len))
+ return -EIO;
+
+ answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+ if (wait_for_completion_interruptible_timeout(&info->completion,
+ answer_tout) <= 0)
+ return -ETIMEDOUT;
+
+ return info->bret_code;
+}
+
+static int alsa_pcm_silence(struct sndfront_info *info, int len)
+{
+ unsigned char *shared_data;
+ unsigned long answer_tout;
+
+ shared_data = info->vcard->buf;
+
+ if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+ return -EFAULT;
+
+ memset(shared_data, 0, len);
+
+ reinit_completion(&info->completion);
+
+ if (sndif_queue_request_write(info, len))
+ return -EIO;
+
+ answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+ if (wait_for_completion_interruptible_timeout(&info->completion,
+ answer_tout) <= 0)
+ return -ETIMEDOUT;
+
+ return info->bret_code;
+}
+
+static int sndif_setup_vcard(struct sndfront_info *info)
+{
+ grant_ref_t gref_head;
+ unsigned long mfn;
+ int ref;
+ int i;
+ int ret;
+
+ info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
+ if (!info->vcard->buf) {
+ ret = -ENOMEM;
+ goto err_ret;
+ }
+
+ ret = gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
+ &gref_head);
+ if (ret)
+ goto err_vstream_free_buf;
+
+ for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) {
+ ref = gnttab_claim_grant_reference(&gref_head);
+ BUG_ON(ref == -ENOSPC);
+
+ mfn = vmalloc_to_mfn(info->vcard->buf + PAGE_SIZE * i);
+
+ gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
+ mfn, 0);
+
+ info->vcard->grefs[i] = ref;
+ }
+
+ gnttab_free_grant_references(gref_head);
+ return ret;
+
+err_vstream_free_buf:
+ vfree(info->vcard->buf);
+ for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+ gnttab_end_foreign_access(info->vcard->grefs[i], 0, 0UL);
+
+ gnttab_free_grant_references(gref_head);
+err_ret:
+ return ret;
+}
+
+static void sndif_cleanup_vcard(struct sndfront_info *info)
+{
+ int i;
+
+ if (info->vcard->buf) {
+ vfree(info->vcard->buf);
+ for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+ gnttab_end_foreign_access(info->vcard->grefs[i],
+ 0, 0UL);
+ }
+}
+
+static int sndif_add_virt_devices(struct sndfront_info *info,
+ unsigned int stream_id)
+{
+ int ret = 0;
+
+ struct vsnd_card *vcard;
+
+ vcard = kmalloc(sizeof(*vcard), GFP_KERNEL);
+
+ if (!vcard)
+ return -ENOMEM;
+
+ vcard->stream_id = stream_id;
+ vcard->fr_info = info;
+
+ info->vcard = vcard;
+
+ sndif_setup_vcard(info);
+
+ return ret;
+}
+
+static void sndif_cleanup_virt_devices(struct sndfront_info *info)
+{
+ if (info->vcard) {
+ sndif_cleanup_vcard(info);
+ kfree(info->vcard);
+ }
+}
+
+static void sndif_free(struct sndfront_info *info, int suspend)
+{
+ /* Free resources associated with old device channel. */
+ if (info->ring_ref != GRANT_INVALID_REF) {
+ gnttab_end_foreign_access(info->ring_ref, 0,
+ (unsigned long)info->ring.sring);
+ info->ring_ref = GRANT_INVALID_REF;
+ info->ring.sring = NULL;
+ }
+ if (info->irq)
+ unbind_from_irqhandler(info->irq, info);
+ info->evtchn = 0;
+ info->irq = 0;
+}
+
+static irqreturn_t sndif_interrupt(int irq, void *data)
+{
+ struct sndif_response *bret;
+ RING_IDX i, rp;
+ unsigned long flags;
+ struct sndfront_info *info = (struct sndfront_info *)data;
+ int error;
+
+ spin_lock_irqsave(&info->io_lock, flags);
+
+ if (unlikely(info->connected != SNDIF_STATE_CONNECTED)) {
+ spin_unlock_irqrestore(&info->io_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ again:
+ rp = info->ring.sring->rsp_prod;
+ rmb(); /* Ensure we see queued responses up to 'rp'. */
+
+ for (i = info->ring.rsp_cons; i != rp; i++) {
+ unsigned long id;
+
+ bret = RING_GET_RESPONSE(&info->ring, i);
+ id = bret->id;
+
+ error = (bret->status == SNDIF_RSP_OKAY) ? 0 : -EIO;
+ switch (bret->operation) {
+ case SNDIF_OP_OPEN:
+ case SNDIF_OP_CLOSE:
+ case SNDIF_OP_WRITE:
+ if (unlikely(bret->status != SNDIF_RSP_OKAY))
+ dev_dbg(&info->xbdev->dev,
+ "snddev data request error: %x\n",
+ bret->status);
+
+ info->bret_code = bret->status;
+ complete(&info->completion);
+ break;
+
+ default:
+ BUG();
+ }
+ }
+
+ info->ring.rsp_cons = i;
+
+ if (i != info->ring.req_prod_pvt) {
+ int more_to_do;
+
+ RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
+ if (more_to_do)
+ goto again;
+ } else {
+ info->ring.sring->rsp_event = i + 1;
+ }
+
+ spin_unlock_irqrestore(&info->io_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int setup_sndring(struct xenbus_device *dev,
+ struct sndfront_info *info)
+{
+ struct sndif_sring *sring;
+ int err;
+
+ info->ring_ref = GRANT_INVALID_REF;
+
+ sring = (struct sndif_sring *)__get_free_page(GFP_NOIO | __GFP_HIGH);
+ if (!sring) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+ return -ENOMEM;
+ }
+ SHARED_RING_INIT(sring);
+ FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+ if (err < 0) {
+ free_page((unsigned long)sring);
+ info->ring.sring = NULL;
+ goto fail;
+ }
+ info->ring_ref = err;
+
+ err = xenbus_alloc_evtchn(dev, &info->evtchn);
+ if (err)
+ goto fail;
+
+ err = bind_evtchn_to_irqhandler(info->evtchn, sndif_interrupt, 0,
+ "sndif", info);
+ if (err <= 0) {
+ xenbus_dev_fatal(dev, err,
+ "bind_evtchn_to_irqhandler failed");
+ goto fail;
+ }
+ info->irq = err;
+
+ return 0;
+fail:
+ sndif_free(info, 0);
+ return err;
+}
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_sndback(struct xenbus_device *dev,
+ struct sndfront_info *info)
+{
+ const char *message = NULL;
+ struct xenbus_transaction xbt;
+ int err;
+
+ /* Create shared ring, alloc event channel. */
+ err = setup_sndring(dev, info);
+ if (err)
+ goto out;
+
+again:
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ goto destroy_sndring;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename,
+ "ring-ref", "%u", info->ring_ref);
+ if (err) {
+ message = "writing ring-ref";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, dev->nodename,
+ "event-channel", "%u", info->evtchn);
+ if (err) {
+ message = "writing event-channel";
+ goto abort_transaction;
+ }
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err) {
+ if (err == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, err, "completing transaction");
+ goto destroy_sndring;
+ }
+
+ xenbus_switch_state(dev, XenbusStateInitialised);
+
+ return 0;
+
+ abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ if (message)
+ xenbus_dev_fatal(dev, err, "%s", message);
+ destroy_sndring:
+ sndif_free(info, 0);
+ out:
+ return err;
+}
+
+static int virtualcard_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ runtime->stop_threshold = runtime->buffer_size + 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ runtime->stop_threshold = runtime->buffer_size;
+ break;
+ }
+ return 0;
+}
+
+static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int err;
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stream_info *vcard_stream;
+
+ vcard_stream = get_vcard_stream(virtualcard, substream);
+
+ if ((runtime->rate != vcard_stream->rate) ||
+ (runtime->channels != vcard_stream->channels) ||
+ (runtime->format != vcard_stream->format)) {
+ if (vcard_stream->opened) {
+ err = alsa_pcm_close(virtualcard->fr_info);
+ if (err)
+ return err;
+
+ /* if closed successfully */
+ vcard_stream->opened = false;
+ }
+ err = alsa_pcm_open(virtualcard->fr_info, runtime->format,
+ runtime->channels, runtime->rate);
+ if (err)
+ return err;
+
+ /* if opened successfully */
+ vcard_stream->rate = runtime->rate;
+ vcard_stream->channels = runtime->channels;
+ vcard_stream->format = runtime->format;
+ vcard_stream->opened = true;
+ }
+ return 0;
+}
+
+static
+snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ snd_pcm_uframes_t buff_size = substream->runtime->buffer_size;
+ struct stream_info *vcard_stream;
+
+ vcard_stream = get_vcard_stream(virtualcard, substream);
+
+ if (vcard_stream->crossed) {
+ snd_pcm_uframes_t hw_base = substream->runtime->hw_ptr_base;
+
+ hw_base += buff_size;
+ if (hw_base >= substream->runtime->boundary)
+ hw_base = 0;
+
+ substream->runtime->hw_ptr_base = hw_base;
+ vcard_stream->crossed = 0;
+ }
+ return vcard_stream->position;
+}
+
+static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return 0;
+}
+
+static int virtualcard_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int virtualcard_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stream_info *vcard_stream;
+
+ vcard_stream = get_vcard_stream(virtualcard, substream);
+
+ vcard_stream->channels = 0;
+ vcard_stream->rate = 0;
+ vcard_stream->position = 0;
+ vcard_stream->crossed = 0;
+ vcard_stream->format = 0;
+ vcard_stream->opened = false;
+
+ runtime->hw = virtualcard->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;
+ return 0;
+}
+
+static int virtualcard_pcm_close(struct snd_pcm_substream *substream)
+{
+ int err;
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+
+ err = alsa_pcm_close(virtualcard->fr_info);
+ if (err)
+ return err;
+
+ get_vcard_stream(virtualcard, substream)->opened = false;
+
+ return 0;
+}
+
+static int virtualcard_pcm_playback_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ void __user *src,
+ snd_pcm_uframes_t count)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stream_info *vcard_stream = &virtualcard->streams[0];
+ snd_pcm_uframes_t stream_pos = vcard_stream->position;
+
+ vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
+ vcard_stream->crossed = count / runtime->buffer_size;
+ return alsa_pcm_write(virtualcard->fr_info, src,
+ frames_to_bytes(runtime, count));
+}
+
+static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stream_info *vcard_stream = &virtualcard->streams[0];
+ snd_pcm_uframes_t stream_pos = vcard_stream->position;
+
+ vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
+ vcard_stream->crossed = count / runtime->buffer_size;
+
+ return alsa_pcm_silence(virtualcard->fr_info,
+ frames_to_bytes(runtime, count));
+}
+
+static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ void __user *dst,
+ snd_pcm_uframes_t count)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stream_info *vcard_stream = &virtualcard->streams[1];
+ snd_pcm_uframes_t stream_pos = vcard_stream->position;
+
+ vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
+ vcard_stream->crossed = count / runtime->buffer_size;
+
+ return 0;
+}
+
+static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
+ .open = virtualcard_pcm_open,
+ .close = virtualcard_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = virtualcard_pcm_hw_params,
+ .hw_free = virtualcard_pcm_hw_free,
+ .prepare = virtualcard_pcm_prepare,
+ .trigger = virtualcard_pcm_trigger,
+ .pointer = virtualcard_pcm_pointer,
+ .copy = virtualcard_pcm_playback_copy,
+ .silence = virtualcard_pcm_playback_silence,
+};
+
+static struct snd_pcm_ops virtualcard_pcm_capture_ops = {
+ .open = virtualcard_pcm_open,
+ .close = virtualcard_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = virtualcard_pcm_hw_params,
+ .hw_free = virtualcard_pcm_hw_free,
+ .prepare = virtualcard_pcm_prepare,
+ .trigger = virtualcard_pcm_trigger,
+ .pointer = virtualcard_pcm_pointer,
+ .copy = virtualcard_pcm_capture_copy,
+};
+
+static int snd_card_virtualcard_pcm(struct snd_virtualcard *virtualcard,
+ int device, int substreams)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(virtualcard->card, "Virtual card PCM", device,
+ substreams, substreams, &pcm);
+ if (err < 0)
+ return err;
+ virtualcard->pcm = pcm;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &virtualcard_pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &virtualcard_pcm_capture_ops);
+ pcm->private_data = virtualcard;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "Virtual card PCM");
+
+ return 0;
+}
+
+/*
+ * mixer interface
+ */
+
+#define VIRTUALCARD_VOLUME(xname, xindex, addr) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_virtualcard_volume_info, \
+ .get = snd_virtualcard_volume_get, \
+ .put = snd_virtualcard_volume_put, \
+ .private_value = addr, \
+ .tlv = { .p = db_scale_virtualcard } \
+}
+
+static int snd_virtualcard_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = -50;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int snd_virtualcard_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
+ int addr = kcontrol->private_value;
+
+ spin_lock_irq(&virtualcard->mixer_lock);
+ ucontrol->value.integer.value[0] = virtualcard->mixer_volume[addr][0];
+ ucontrol->value.integer.value[1] = virtualcard->mixer_volume[addr][1];
+ spin_unlock_irq(&virtualcard->mixer_lock);
+ return 0;
+}
+
+static int snd_virtualcard_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
+ int change, addr = kcontrol->private_value;
+ int left, right;
+
+ left = ucontrol->value.integer.value[0];
+ if (left < -50)
+ left = -50;
+ if (left > 100)
+ left = 100;
+ right = ucontrol->value.integer.value[1];
+ if (right < -50)
+ right = -50;
+ if (right > 100)
+ right = 100;
+ spin_lock_irq(&virtualcard->mixer_lock);
+ change = virtualcard->mixer_volume[addr][0] != left ||
+ virtualcard->mixer_volume[addr][1] != right;
+ virtualcard->mixer_volume[addr][0] = left;
+ virtualcard->mixer_volume[addr][1] = right;
+ spin_unlock_irq(&virtualcard->mixer_lock);
+ return change;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_virtualcard, -4500, 30, 0);
+
+#define VIRTUALCARD_CAPSRC(xname, xindex, addr) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_virtualcard_capsrc_info, \
+ .get = snd_virtualcard_capsrc_get, \
+ .put = snd_virtualcard_capsrc_put, \
+ .private_value = addr \
+}
+
+#define snd_virtualcard_capsrc_info snd_ctl_boolean_stereo_info
+
+static int snd_virtualcard_capsrc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
+ int addr = kcontrol->private_value;
+
+ spin_lock_irq(&virtualcard->mixer_lock);
+ ucontrol->value.integer.value[0] = virtualcard->capture_source[addr][0];
+ ucontrol->value.integer.value[1] = virtualcard->capture_source[addr][1];
+ spin_unlock_irq(&virtualcard->mixer_lock);
+ return 0;
+}
+
+static int snd_virtualcard_capsrc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
+ int change, addr = kcontrol->private_value;
+ int left, right;
+
+ left = ucontrol->value.integer.value[0] & 1;
+ right = ucontrol->value.integer.value[1] & 1;
+ spin_lock_irq(&virtualcard->mixer_lock);
+ change = virtualcard->capture_source[addr][0] != left &&
+ virtualcard->capture_source[addr][1] != right;
+ virtualcard->capture_source[addr][0] = left;
+ virtualcard->capture_source[addr][1] = right;
+ spin_unlock_irq(&virtualcard->mixer_lock);
+ return change;
+}
+
+static struct snd_kcontrol_new snd_virtualcard_controls[] = {
+VIRTUALCARD_VOLUME("Master Out Volume", 0, MIXER_ADDR_MASTER_OUT),
+VIRTUALCARD_CAPSRC("Master Out Switch", 0, MIXER_ADDR_MASTER_OUT),
+VIRTUALCARD_VOLUME("Master In Volume", 0, MIXER_ADDR_MASTER_IN),
+VIRTUALCARD_CAPSRC("Master In Switch", 0, MIXER_ADDR_MASTER_IN),
+};
+
+static int snd_card_virtualcard_new_mixer(struct snd_virtualcard *virtualcard)
+{
+ struct snd_card *card = virtualcard->card;
+ struct snd_kcontrol *kcontrol;
+ unsigned int idx;
+ int err;
+
+ spin_lock_init(&virtualcard->mixer_lock);
+ strcpy(card->mixername, "Virtual card Mixer");
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_virtualcard_controls); idx++) {
+ kcontrol = snd_ctl_new1(&snd_virtualcard_controls[idx],
+ virtualcard);
+ err = snd_ctl_add(card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* #define CONFIG_SND_DEBUG */
+/* #define CONFIG_PROC_FS */
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+/*
+ * proc interface
+ */
+static void print_formats(struct snd_virtualcard *virtualcard,
+ struct snd_info_buffer *buffer)
+{
+ int i;
+
+ for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ if (virtualcard->pcm_hw.formats & (1ULL << i))
+ snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
+ }
+}
+
+static void print_rates(struct snd_virtualcard *virtualcard,
+ struct snd_info_buffer *buffer)
+{
+ static int rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+ 64000, 88200, 96000, 176400, 192000,
+ };
+ int i;
+
+ if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
+ snd_iprintf(buffer, " continuous");
+ if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
+ snd_iprintf(buffer, " knot");
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (virtualcard->pcm_hw.rates & (1 << i))
+ snd_iprintf(buffer, " %d", rates[i]);
+}
+
+#define get_virtualcard_int_ptr(virtualcard, ofs) \
+ (unsigned int *)((char *)&((virtualcard)->pcm_hw) + (ofs))
+#define get_virtualcard_ll_ptr(virtualcard, ofs) \
+ (unsigned long long *)((char *)&((virtualcard)->pcm_hw) + (ofs))
+
+struct virtualcard_hw_field {
+ const char *name;
+ const char *format;
+ unsigned int offset;
+ unsigned int size;
+};
+
+#define FIELD_ENTRY(item, fmt) { \
+ .name = #item, \
+ .format = fmt, \
+ .offset = offsetof(struct snd_pcm_hardware, item), \
+ .size = sizeof(virtualcard_pcm_hardware.item) \
+}
+
+static struct virtualcard_hw_field fields[] = {
+ FIELD_ENTRY(formats, "%#llx"),
+ FIELD_ENTRY(rates, "%#x"),
+ FIELD_ENTRY(rate_min, "%d"),
+ FIELD_ENTRY(rate_max, "%d"),
+ FIELD_ENTRY(channels_min, "%d"),
+ FIELD_ENTRY(channels_max, "%d"),
+ FIELD_ENTRY(buffer_bytes_max, "%ld"),
+ FIELD_ENTRY(period_bytes_min, "%ld"),
+ FIELD_ENTRY(period_bytes_max, "%ld"),
+ FIELD_ENTRY(periods_min, "%d"),
+ FIELD_ENTRY(periods_max, "%d"),
+};
+
+static void virtualcard_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_virtualcard *virtualcard = entry->private_data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ snd_iprintf(buffer, "%s ", fields[i].name);
+ if (fields[i].size == sizeof(int))
+ snd_iprintf(buffer, fields[i].format,
+ *get_virtualcard_int_ptr(virtualcard,
+ fields[i].offset));
+ else
+ snd_iprintf(buffer, fields[i].format,
+ *get_virtualcard_ll_ptr(virtualcard,
+ fields[i].offset));
+ if (!strcmp(fields[i].name, "formats"))
+ print_formats(virtualcard, buffer);
+ else if (!strcmp(fields[i].name, "rates"))
+ print_rates(virtualcard, buffer);
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void virtualcard_proc_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_virtualcard *virtualcard = entry->private_data;
+ char line[64];
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ char item[20];
+ const char *ptr;
+ unsigned long long val;
+ unsigned int offset;
+ int i;
+
+ ptr = snd_info_get_str(item, line, sizeof(item));
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ if (!strcmp(item, fields[i].name))
+ break;
+ }
+ if (i >= ARRAY_SIZE(fields))
+ continue;
+ snd_info_get_str(item, ptr, sizeof(item));
+ if (kstrtoull(item, 0, &val))
+ continue;
+
+ offset = fields[i].offset;
+ if (fields[i].size == sizeof(int))
+ *get_virtualcard_int_ptr(virtualcard, offset) = val;
+ else
+ *get_virtualcard_ll_ptr(virtualcard, offset) = val;
+ }
+}
+
+static void virtualcard_proc_init(struct snd_virtualcard *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "virtualcard_pcm", &entry)) {
+ snd_info_set_text_ops(entry, chip, virtualcard_proc_read);
+ entry->c.text.write = virtualcard_proc_write;
+ entry->mode |= S_IWUSR;
+ entry->private_data = chip;
+ }
+}
+#else
+#define virtualcard_proc_init(x)
+#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+
+static int snd_virtualcard_probe(struct platform_device *devptr)
+{
+ struct snd_card *card;
+ struct snd_virtualcard *virtualcard;
+ struct virtualcard_model *m = NULL;
+ int err;
+ int dev = devptr->id;
+ struct snd_pcm_hardware *pcm_hw;
+
+ err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_virtualcard), &card);
+ if (err < 0)
+ return err;
+ virtualcard = card->private_data;
+ virtualcard->card = card;
+ virtualcard->fr_info =
+ (*((struct sndfront_info **)devptr->dev.platform_data));
+
+ m = virtualcard_models[0];
+
+ err = snd_card_virtualcard_pcm(virtualcard, 0, 1);
+ if (err < 0)
+ goto __nodev;
+
+ virtualcard->pcm_hw = virtualcard_pcm_hardware;
+ pcm_hw = &virtualcard->pcm_hw;
+ if (m) {
+ if (m->formats)
+ pcm_hw->formats = m->formats;
+ if (m->buffer_bytes_max)
+ pcm_hw->buffer_bytes_max = m->buffer_bytes_max;
+ if (m->period_bytes_min)
+ pcm_hw->period_bytes_min = m->period_bytes_min;
+ if (m->period_bytes_max)
+ pcm_hw->period_bytes_max = m->period_bytes_max;
+ if (m->periods_min)
+ pcm_hw->periods_min = m->periods_min;
+ if (m->periods_max)
+ pcm_hw->periods_max = m->periods_max;
+ if (m->rates)
+ pcm_hw->rates = m->rates;
+ if (m->rate_min)
+ pcm_hw->rate_min = m->rate_min;
+ if (m->rate_max)
+ pcm_hw->rate_max = m->rate_max;
+ if (m->channels_min)
+ pcm_hw->channels_min = m->channels_min;
+ if (m->channels_max)
+ pcm_hw->channels_max = m->channels_max;
+ }
+
+ err = snd_card_virtualcard_new_mixer(virtualcard);
+ if (err < 0)
+ goto __nodev;
+ strcpy(card->driver, "Virtual card");
+ strcpy(card->shortname, "Virtual card");
+ sprintf(card->longname, "Virtual card %i", dev + 1);
+
+ virtualcard_proc_init(virtualcard);
+
+ snd_card_set_dev(card, &devptr->dev);
+
+ err = snd_card_register(card);
+ if (err == 0) {
+ platform_set_drvdata(devptr, card);
+ return 0;
+ }
+__nodev:
+ snd_card_free(card);
+ return err;
+}
+
+static int snd_virtualcard_remove(struct platform_device *devptr)
+{
+ snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(devptr, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snd_virtualcard_suspend(struct device *pdev)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_virtualcard *virtualcard = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(virtualcard->pcm);
+ return 0;
+}
+
+static int snd_virtualcard_resume(struct device *pdev)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_virtualcard_pm, snd_virtualcard_suspend,
+ snd_virtualcard_resume);
+#define SND_VIRTUALCARD_PM_OPS (&snd_virtualcard_pm)
+#else
+#define SND_VIRTUALCARD_PM_OPS NULL
+#endif
+
+#define SND_VIRTUALCARD_DRIVER "snd_virtualcard"
+
+static struct platform_driver snd_virtualcard_driver = {
+ .probe = snd_virtualcard_probe,
+ .remove = snd_virtualcard_remove,
+ .driver = {
+ .name = SND_VIRTUALCARD_DRIVER,
+ .owner = THIS_MODULE,
+ .pm = SND_VIRTUALCARD_PM_OPS,
+ },
+};
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures and the ring buffer for communication with the backend, and
+ * inform the backend of the appropriate details for those. Switch to
+ * Initialised state.
+ */
+static int sndfront_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int err;
+ struct sndfront_info *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+ return -ENOMEM;
+ }
+
+ mutex_init(&info->mutex);
+ spin_lock_init(&info->io_lock);
+ init_completion(&info->completion);
+ info->xbdev = dev;
+
+ dev_set_drvdata(&dev->dev, info);
+
+ err = talk_to_sndback(dev, info);
+ if (err)
+ goto err_free_info;
+
+ info->card_dev = platform_device_register_data(NULL,
+ SND_VIRTUALCARD_DRIVER,
+ -1, &info, sizeof(info));
+ if (IS_ERR(info->card_dev)) {
+ err = -ENODEV;
+ goto err_free_info;
+ }
+ return 0;
+
+err_free_info:
+ kfree(info);
+ dev_set_drvdata(&dev->dev, NULL);
+ return err;
+}
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart. We tear down our blkif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int sndfront_resume(struct xenbus_device *dev)
+{
+ struct sndfront_info *info = dev_get_drvdata(&dev->dev);
+ int err;
+
+ dev_dbg(&dev->dev, "sndfront_resume: %s\n", dev->nodename);
+
+ sndif_free(info, info->connected == SNDIF_STATE_CONNECTED);
+
+ err = talk_to_sndback(dev, info);
+ if (info->connected == SNDIF_STATE_SUSPENDED && !err)
+ info->connected = SNDIF_STATE_CONNECTED;
+
+ return err;
+}
+
+static void
+sndfront_closing(struct sndfront_info *info)
+{
+ struct xenbus_device *xbdev = info->xbdev;
+
+ mutex_lock(&info->mutex);
+
+ if (xbdev->state == XenbusStateClosing) {
+ mutex_unlock(&info->mutex);
+ return;
+ }
+
+ mutex_unlock(&info->mutex);
+
+ xenbus_frontend_closed(xbdev);
+ sndif_cleanup_virt_devices(info);
+}
+
+/*
+ * Invoked when the backend is finally 'ready' (and has told produced
+ * the details about the physical device - #sectors, size, etc).
+ */
+static void sndfront_connect(struct sndfront_info *info)
+{
+ unsigned int stream_id;
+ int err;
+
+ switch (info->connected) {
+ case SNDIF_STATE_CONNECTED:
+
+ /* fall through */
+ case SNDIF_STATE_SUSPENDED:
+ return;
+
+ default:
+ break;
+ }
+
+ dev_dbg(&info->xbdev->dev, "%s:%s.\n",
+ __func__, info->xbdev->otherend);
+
+ xenbus_switch_state(info->xbdev, XenbusStateConnected);
+
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "stream_id", "%u",
+ &stream_id, NULL);
+ if (err)
+ return;
+
+ err = sndif_add_virt_devices(info, stream_id);
+ if (err)
+ return;
+
+ spin_lock_irq(&info->io_lock);
+ info->connected = SNDIF_STATE_CONNECTED;
+ spin_unlock_irq(&info->io_lock);
+}
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void sndback_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ struct sndfront_info *info = dev_get_drvdata(&dev->dev);
+
+ dev_dbg(&dev->dev, "sndfront:sndback_changed to state %d.\n",
+ backend_state);
+
+ switch (backend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitWait:
+ case XenbusStateInitialised:
+ case XenbusStateReconfiguring:
+ case XenbusStateReconfigured:
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateConnected:
+ sndfront_connect(info);
+ break;
+
+ case XenbusStateClosing:
+ sndfront_closing(info);
+ break;
+ }
+}
+
+static int sndfront_remove(struct xenbus_device *xbdev)
+{
+ struct sndfront_info *info = dev_get_drvdata(&xbdev->dev);
+
+ dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
+
+ platform_device_unregister(info->card_dev);
+
+ sndif_free(info, 0);
+
+ return 0;
+}
+
+static const struct xenbus_device_id xen_snd_ids[] = {
+ { "vsnd" },
+ { "" }
+};
+
+static struct xenbus_driver xen_snd_driver = {
+ .name = "xensnd",
+ .ids = xen_snd_ids,
+ .probe = sndfront_probe,
+ .remove = sndfront_remove,
+ .resume = sndfront_resume,
+ .otherend_changed = sndback_changed,
+};
+
+static int __init xen_snd_front_init(void)
+{
+ int ret = 0;
+
+ /*FIXME: xen_pv_domain() should be here, but ARM hardcoded to hvm*/
+ if (!xen_domain())
+ return -ENODEV;
+
+ /* Nothing to do if running in dom0. */
+ if (xen_initial_domain())
+ return -ENODEV;
+
+ ret = xenbus_register_frontend(&xen_snd_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&snd_virtualcard_driver);
+
+ return ret;
+}
+
+static void __exit xen_snd_front_cleanup(void)
+{
+ xenbus_unregister_driver(&xen_snd_driver);
+ platform_driver_unregister(&snd_virtualcard_driver);
+}
+
+module_init(xen_snd_front_init);
+module_exit(xen_snd_front_cleanup);
+
+MODULE_DESCRIPTION("Xen virtual audio device frontend");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vsnd");
--
1.9.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] xen-sndfront: add capture support
2015-01-20 9:03 [PATCH v2 0/2] Add sound frontend driver Oleksandr Dmytryshyn
2015-01-20 9:03 ` [PATCH v2 1/2] xen-sndfront: add " Oleksandr Dmytryshyn
@ 2015-01-20 9:04 ` Oleksandr Dmytryshyn
1 sibling, 0 replies; 5+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-20 9:04 UTC (permalink / raw)
To: xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela
From: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
Now both play and capture is supported.
Signed-off-by: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
---
sound/drivers/xen-sndfront.c | 193 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 164 insertions(+), 29 deletions(-)
diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
index d0367c2..054e535 100644
--- a/sound/drivers/xen-sndfront.c
+++ b/sound/drivers/xen-sndfront.c
@@ -98,7 +98,6 @@ MODULE_PARM_DESC(model, "Soundcard model.");
#define MIXER_ADDR_LAST MIXER_ADDR_MASTER_OUT
struct vsnd_card {
- struct sndfront_info *fr_info;
unsigned int stream_id;
grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST];
unsigned char *buf;
@@ -123,7 +122,13 @@ struct sndfront_info {
unsigned int evtchn, irq;
struct vsnd_card *vcard;
int bret_code;
+};
+
+struct vsnd_sndfront {
+ int count;
+ spinlock_t lock; /* protect 'count' member */
struct platform_device *card_dev;
+ struct sndfront_info *infos[2];
};
#define GRANT_INVALID_REF 0
@@ -160,7 +165,6 @@ struct snd_virtualcard {
spinlock_t mixer_lock; /* protect mixer settings */
int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2];
- struct sndfront_info *fr_info;
struct stream_info streams[2];
};
@@ -206,6 +210,8 @@ static struct snd_pcm_hardware virtualcard_pcm_hardware = {
.fifo_size = 0,
};
+static struct vsnd_sndfront snd_fronts;
+
static inline
struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard,
struct snd_pcm_substream *substream)
@@ -216,6 +222,22 @@ struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard,
return &virtualcard->streams[1];
}
+static inline
+struct sndfront_info *get_sndfront_info(struct snd_pcm_substream *substream)
+{
+ struct sndfront_info *res = NULL;
+ int stream = substream->stream;
+
+ spin_lock_irq(&snd_fronts.lock);
+ if ((stream == SNDRV_PCM_STREAM_PLAYBACK) && (snd_fronts.count > 0))
+ res = snd_fronts.infos[0];
+ else if ((stream == SNDRV_PCM_STREAM_CAPTURE) && (snd_fronts.count > 1))
+ res = snd_fronts.infos[1];
+ spin_unlock_irq(&snd_fronts.lock);
+
+ return res;
+}
+
static unsigned long vmalloc_to_mfn(void *address)
{
return pfn_to_mfn(vmalloc_to_pfn(address));
@@ -234,7 +256,8 @@ static inline void flush_requests(struct sndfront_info *info)
static int sndif_queue_request_open(struct sndfront_info *info,
snd_pcm_format_t format,
unsigned int channels,
- unsigned int rate)
+ unsigned int rate,
+ unsigned int stream)
{
struct sndif_request *req;
@@ -248,6 +271,7 @@ static int sndif_queue_request_open(struct sndfront_info *info,
req->u.open.snd_params.format = format;
req->u.open.snd_params.channels = channels;
req->u.open.snd_params.rate = rate;
+ req->u.open.stream = stream;
info->ring.req_prod_pvt++;
flush_requests(info);
@@ -300,16 +324,48 @@ static int sndif_queue_request_write(struct sndfront_info *info,
return 0;
}
+static int sndif_queue_request_read(struct sndfront_info *info,
+ unsigned int len)
+{
+ struct sndif_request *req;
+ grant_ref_t *gref;
+ int i;
+
+ if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
+ return 1;
+
+ req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
+
+ req->operation = SNDIF_OP_READ;
+ req->u.rw.id = info->vcard->stream_id;
+
+ req->u.rw.len = len;
+
+ gref = info->vcard->grefs;
+
+ for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+ req->u.rw.gref[i] = gref[i];
+
+ info->ring.req_prod_pvt++;
+
+ flush_requests(info);
+ return 0;
+}
+
static int alsa_pcm_open(struct sndfront_info *info,
snd_pcm_format_t format,
unsigned int channels,
- unsigned int rate)
+ unsigned int rate,
+ unsigned int stream)
{
unsigned long answer_tout;
+ if (!info)
+ return -EFAULT;
+
reinit_completion(&info->completion);
- if (sndif_queue_request_open(info, format, channels, rate))
+ if (sndif_queue_request_open(info, format, channels, rate, stream))
return -EIO;
answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
@@ -324,6 +380,9 @@ static int alsa_pcm_close(struct sndfront_info *info)
{
unsigned long answer_tout;
+ if (!info)
+ return -EFAULT;
+
reinit_completion(&info->completion);
if (sndif_queue_request_close(info))
@@ -343,6 +402,9 @@ static int alsa_pcm_write(struct sndfront_info *info, char __user *buf,
unsigned char *shared_data;
unsigned long answer_tout;
+ if (!info)
+ return -EFAULT;
+
shared_data = info->vcard->buf;
if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
@@ -364,6 +426,35 @@ static int alsa_pcm_write(struct sndfront_info *info, char __user *buf,
return info->bret_code;
}
+static int alsa_pcm_read(struct sndfront_info *info, char __user *buf,
+ int len)
+{
+ unsigned char *shared_data;
+ unsigned long answer_tout;
+
+ if (!info)
+ return -EFAULT;
+
+ shared_data = info->vcard->buf;
+
+ if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+ return -EFAULT;
+
+ reinit_completion(&info->completion);
+
+ if (sndif_queue_request_read(info, len))
+ return -EIO;
+
+ answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+ if (wait_for_completion_interruptible_timeout(&info->completion,
+ answer_tout) <= 0)
+ return -ETIMEDOUT;
+
+ if (copy_to_user(buf, shared_data, len))
+ return -EFAULT;
+
+ return info->bret_code;
+}
static int alsa_pcm_silence(struct sndfront_info *info, int len)
{
unsigned char *shared_data;
@@ -458,7 +549,6 @@ static int sndif_add_virt_devices(struct sndfront_info *info,
return -ENOMEM;
vcard->stream_id = stream_id;
- vcard->fr_info = info;
info->vcard = vcard;
@@ -520,6 +610,7 @@ static irqreturn_t sndif_interrupt(int irq, void *data)
case SNDIF_OP_OPEN:
case SNDIF_OP_CLOSE:
case SNDIF_OP_WRITE:
+ case SNDIF_OP_READ:
if (unlikely(bret->status != SNDIF_RSP_OKAY))
dev_dbg(&info->xbdev->dev,
"snddev data request error: %x\n",
@@ -677,16 +768,19 @@ static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream)
if ((runtime->rate != vcard_stream->rate) ||
(runtime->channels != vcard_stream->channels) ||
(runtime->format != vcard_stream->format)) {
+ struct sndfront_info *info = get_sndfront_info(substream);
+
if (vcard_stream->opened) {
- err = alsa_pcm_close(virtualcard->fr_info);
+ err = alsa_pcm_close(info);
if (err)
return err;
/* if closed successfully */
vcard_stream->opened = false;
}
- err = alsa_pcm_open(virtualcard->fr_info, runtime->format,
- runtime->channels, runtime->rate);
+
+ err = alsa_pcm_open(info, runtime->format, runtime->channels,
+ runtime->rate, substream->stream);
if (err)
return err;
@@ -699,8 +793,8 @@ static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static
-snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+virtualcard_pcm_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
snd_pcm_uframes_t buff_size = substream->runtime->buffer_size;
@@ -721,6 +815,23 @@ snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream)
return vcard_stream->position;
}
+static snd_pcm_uframes_t
+virtualcard_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ snd_pcm_uframes_t buff_size = substream->runtime->buffer_size, res;
+ struct stream_info *vcard_stream;
+
+ vcard_stream = get_vcard_stream(virtualcard, substream);
+
+#ifdef MAX_CAPTURE_SIZE
+ res = (vcard_stream->position + MAX_CAPTURE_SIZE) % buff_size;
+#else
+ res = (vcard_stream->position + buff_size / 2) % buff_size;
+#endif
+ return res;
+}
+
static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
@@ -763,8 +874,9 @@ static int virtualcard_pcm_close(struct snd_pcm_substream *substream)
{
int err;
struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+ struct sndfront_info *info = get_sndfront_info(substream);
- err = alsa_pcm_close(virtualcard->fr_info);
+ err = alsa_pcm_close(info);
if (err)
return err;
@@ -782,11 +894,11 @@ static int virtualcard_pcm_playback_copy(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct stream_info *vcard_stream = &virtualcard->streams[0];
snd_pcm_uframes_t stream_pos = vcard_stream->position;
+ struct sndfront_info *info = get_sndfront_info(substream);
vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
vcard_stream->crossed = count / runtime->buffer_size;
- return alsa_pcm_write(virtualcard->fr_info, src,
- frames_to_bytes(runtime, count));
+ return alsa_pcm_write(info, src, frames_to_bytes(runtime, count));
}
static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream,
@@ -797,12 +909,12 @@ static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct stream_info *vcard_stream = &virtualcard->streams[0];
snd_pcm_uframes_t stream_pos = vcard_stream->position;
+ struct sndfront_info *info = get_sndfront_info(substream);
vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
vcard_stream->crossed = count / runtime->buffer_size;
- return alsa_pcm_silence(virtualcard->fr_info,
- frames_to_bytes(runtime, count));
+ return alsa_pcm_silence(info, frames_to_bytes(runtime, count));
}
static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream,
@@ -814,11 +926,12 @@ static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct stream_info *vcard_stream = &virtualcard->streams[1];
snd_pcm_uframes_t stream_pos = vcard_stream->position;
+ struct sndfront_info *info = get_sndfront_info(substream);
vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
vcard_stream->crossed = count / runtime->buffer_size;
- return 0;
+ return alsa_pcm_read(info, dst, frames_to_bytes(runtime, count));
}
static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
@@ -829,7 +942,7 @@ static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
.hw_free = virtualcard_pcm_hw_free,
.prepare = virtualcard_pcm_prepare,
.trigger = virtualcard_pcm_trigger,
- .pointer = virtualcard_pcm_pointer,
+ .pointer = virtualcard_pcm_playback_pointer,
.copy = virtualcard_pcm_playback_copy,
.silence = virtualcard_pcm_playback_silence,
};
@@ -842,7 +955,7 @@ static struct snd_pcm_ops virtualcard_pcm_capture_ops = {
.hw_free = virtualcard_pcm_hw_free,
.prepare = virtualcard_pcm_prepare,
.trigger = virtualcard_pcm_trigger,
- .pointer = virtualcard_pcm_pointer,
+ .pointer = virtualcard_pcm_capture_pointer,
.copy = virtualcard_pcm_capture_copy,
};
@@ -1160,8 +1273,6 @@ static int snd_virtualcard_probe(struct platform_device *devptr)
return err;
virtualcard = card->private_data;
virtualcard->card = card;
- virtualcard->fr_info =
- (*((struct sndfront_info **)devptr->dev.platform_data));
m = virtualcard_models[0];
@@ -1271,7 +1382,7 @@ static struct platform_driver snd_virtualcard_driver = {
static int sndfront_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id)
{
- int err;
+ int err, count;
struct sndfront_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -1291,12 +1402,26 @@ static int sndfront_probe(struct xenbus_device *dev,
if (err)
goto err_free_info;
- info->card_dev = platform_device_register_data(NULL,
- SND_VIRTUALCARD_DRIVER,
- -1, &info, sizeof(info));
- if (IS_ERR(info->card_dev)) {
- err = -ENODEV;
- goto err_free_info;
+ spin_lock_irq(&snd_fronts.lock);
+ snd_fronts.infos[snd_fronts.count] = info;
+ snd_fronts.count++;
+ count = snd_fronts.count;
+ spin_unlock_irq(&snd_fronts.lock);
+
+ if (count == 1) {
+ struct platform_device *card_dev;
+
+ card_dev = platform_device_register_data(NULL,
+ SND_VIRTUALCARD_DRIVER,
+ -1, NULL, 0);
+ snd_fronts.card_dev = card_dev;
+ if (IS_ERR(snd_fronts.card_dev)) {
+ spin_lock_irq(&snd_fronts.lock);
+ snd_fronts.count = 0;
+ spin_unlock_irq(&snd_fronts.lock);
+ err = -ENODEV;
+ goto err_free_info;
+ }
}
return 0;
@@ -1418,11 +1543,19 @@ static void sndback_changed(struct xenbus_device *dev,
static int sndfront_remove(struct xenbus_device *xbdev)
{
+ int count;
struct sndfront_info *info = dev_get_drvdata(&xbdev->dev);
dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
- platform_device_unregister(info->card_dev);
+ spin_lock_irq(&snd_fronts.lock);
+ snd_fronts.count--;
+ count = snd_fronts.count;
+ snd_fronts.infos[count] = NULL;
+ spin_lock_irq(&snd_fronts.lock);
+
+ if (!count)
+ platform_device_unregister(snd_fronts.card_dev);
sndif_free(info, 0);
@@ -1447,6 +1580,8 @@ static int __init xen_snd_front_init(void)
{
int ret = 0;
+ snd_fronts.count = 0;
+ spin_lock_init(&snd_fronts.lock);
/*FIXME: xen_pv_domain() should be here, but ARM hardcoded to hvm*/
if (!xen_domain())
return -ENODEV;
--
1.9.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] xen-sndfront: add sound frontend driver
2015-01-20 9:03 ` [PATCH v2 1/2] xen-sndfront: add " Oleksandr Dmytryshyn
@ 2015-01-21 15:39 ` Jaroslav Kysela
2015-01-22 10:24 ` Oleksandr Dmytryshyn
0 siblings, 1 reply; 5+ messages in thread
From: Jaroslav Kysela @ 2015-01-21 15:39 UTC (permalink / raw)
To: Oleksandr Dmytryshyn, xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko
Dne 20.1.2015 v 10:03 Oleksandr Dmytryshyn napsal(a):
> This is Para-virtual sound driver.
>
> Frontend driver registers an virtual sound card
> and sends an PCM streams to the backend driver.
> Backend driver is an user-space application and
> uses ALSA with dmix plugin to play audio.
>
> Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
> Signed-off-by: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
> +static struct snd_kcontrol_new snd_virtualcard_controls[] = {
> +VIRTUALCARD_VOLUME("Master Out Volume", 0, MIXER_ADDR_MASTER_OUT),
> +VIRTUALCARD_CAPSRC("Master Out Switch", 0, MIXER_ADDR_MASTER_OUT),
> +VIRTUALCARD_VOLUME("Master In Volume", 0, MIXER_ADDR_MASTER_IN),
> +VIRTUALCARD_CAPSRC("Master In Switch", 0, MIXER_ADDR_MASTER_IN),
Master In / Out ? Why ? I does not follow the standard naming.
I would prefer 'PCM' and 'PCM Input' or so...
Jaroslav
--
Jaroslav Kysela <perex@perex.cz>
Linux Kernel Sound Maintainer
ALSA Project; Red Hat, Inc.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] xen-sndfront: add sound frontend driver
2015-01-21 15:39 ` Jaroslav Kysela
@ 2015-01-22 10:24 ` Oleksandr Dmytryshyn
0 siblings, 0 replies; 5+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-22 10:24 UTC (permalink / raw)
To: Jaroslav Kysela; +Cc: Takashi Iwai, Iurii Konovalenko, xen-devel
On Wed, Jan 21, 2015 at 5:39 PM, Jaroslav Kysela <perex@perex.cz> wrote:
> Dne 20.1.2015 v 10:03 Oleksandr Dmytryshyn napsal(a):
>> This is Para-virtual sound driver.
>>
>> Frontend driver registers an virtual sound card
>> and sends an PCM streams to the backend driver.
>> Backend driver is an user-space application and
>> uses ALSA with dmix plugin to play audio.
>>
>> Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
>> Signed-off-by: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
>
>> +static struct snd_kcontrol_new snd_virtualcard_controls[] = {
>> +VIRTUALCARD_VOLUME("Master Out Volume", 0, MIXER_ADDR_MASTER_OUT),
>> +VIRTUALCARD_CAPSRC("Master Out Switch", 0, MIXER_ADDR_MASTER_OUT),
>> +VIRTUALCARD_VOLUME("Master In Volume", 0, MIXER_ADDR_MASTER_IN),
>> +VIRTUALCARD_CAPSRC("Master In Switch", 0, MIXER_ADDR_MASTER_IN),
>
> Master In / Out ? Why ? I does not follow the standard naming.
>
> I would prefer 'PCM' and 'PCM Input' or so...
I'll rename this in the next patch-set.
> Jaroslav
>
> --
> Jaroslav Kysela <perex@perex.cz>
> Linux Kernel Sound Maintainer
> ALSA Project; Red Hat, Inc.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2015-01-22 10:24 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-20 9:03 [PATCH v2 0/2] Add sound frontend driver Oleksandr Dmytryshyn
2015-01-20 9:03 ` [PATCH v2 1/2] xen-sndfront: add " Oleksandr Dmytryshyn
2015-01-21 15:39 ` Jaroslav Kysela
2015-01-22 10:24 ` Oleksandr Dmytryshyn
2015-01-20 9:04 ` [PATCH v2 2/2] xen-sndfront: add capture support Oleksandr Dmytryshyn
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.