All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Add sound frontend driver
@ 2015-01-19  8:19 Oleksandr Dmytryshyn
  2015-01-19  8:19 ` [PATCH 1/3] xen-sndfront: add " Oleksandr Dmytryshyn
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19  8:19 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.

Iurii Konovalenko (2):
  xen-sndfront: switch to the v2 driver
  xen-sndfront: add capture support

Oleksandr Dmytryshyn (1):
  xen-sndfront: add sound frontend driver

 include/xen/interface/io/sndif.h |  140 ++++
 sound/drivers/Kconfig            |   10 +
 sound/drivers/Makefile           |    2 +
 sound/drivers/xen-sndfront.c     | 1603 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 1755 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] 10+ messages in thread

* [PATCH 1/3] xen-sndfront: add sound frontend driver
  2015-01-19  8:19 [PATCH 0/3] Add sound frontend driver Oleksandr Dmytryshyn
@ 2015-01-19  8:19 ` Oleksandr Dmytryshyn
  2015-01-19 10:55   ` David Vrabel
  2015-01-19  8:19 ` [PATCH 2/3] xen-sndfront: switch to the v2 driver Oleksandr Dmytryshyn
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19  8:19 UTC (permalink / raw)
  To: xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela

This is Para-virtual sound driver.

This driver creates sound files in /dev/snd/:
controlC0, pcmC0D0p, etc. Then it intercepts some
IOCTLs and redirects them to the backend driver.
Backend driver is build-in the kernel. It issues
those IOCTLs on the real sound files and returns
the result to the frontend driver.

Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
---
 include/xen/interface/io/sndif.h |   98 ++++
 sound/drivers/Kconfig            |    9 +
 sound/drivers/Makefile           |    2 +
 sound/drivers/xen-sndfront.c     | 1117 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 1226 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..9ef0c41
--- /dev/null
+++ b/include/xen/interface/io/sndif.h
@@ -0,0 +1,98 @@
+/******************************************************************************
+ * sndif.h
+ *
+ * Unified sound-device I/O interface for Xen guest OSes.
+ *
+ */
+#ifndef __XEN_PUBLIC_IO_SNDIF_H__
+#define __XEN_PUBLIC_IO_SNDIF_H__
+
+#include <xen/interface/io/ring.h>
+#include <xen/interface/grant_table.h>
+
+/*
+ * REQUEST CODES.
+ */
+#define SNDIF_OP_OPEN			0
+#define SNDIF_OP_CLOSE			1
+#define SNDIF_OP_READ			2
+#define SNDIF_OP_WRITE			3
+#define SNDIF_OP_IOCTL			4
+
+#define SNDIF_DEV_TYPE_CONTROL		0
+#define SNDIF_DEV_TYPE_STREAM_PLAY	1
+#define SNDIF_DEV_TYPE_STREAM_CAPTURE	2
+
+#define SNDIF_MAX_PAGES_PER_REQUEST	10
+
+#define SNDIF_DEV_ID_CNT		5
+
+/*
+ * STATUS RETURN CODES.
+ */
+ /* Operation failed for some unspecified reason (-EIO). */
+#define SNDIF_RSP_ERROR       -1
+ /* Operation completed successfully. */
+#define SNDIF_RSP_OKAY         0
+
+struct sndif_request_open {
+	unsigned int dev_num;
+	unsigned int card_num;
+	unsigned int dev_type;
+	unsigned int _pad1;
+	uint64_t     id;           /* private guest value, echoed in resp  */
+	unsigned int _pad2;
+	unsigned int _pad3;
+} __attribute__((__packed__));
+
+struct sndif_request_ioctl {
+	unsigned int dev_num;
+	unsigned int card_num;
+	unsigned int dev_type;
+	unsigned int cmd;
+	uint64_t     id;           /* private guest value, echoed in resp  */
+	unsigned int add_len_to;
+	unsigned int add_len_from;
+	grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
+} __attribute__((__packed__));
+
+struct sndif_request_rw {
+	unsigned int dev_num;
+	unsigned int card_num;
+	unsigned int dev_type;
+	unsigned int _pad1;
+	uint64_t     id;           /* private guest value, echoed in resp  */
+	unsigned int len;
+	unsigned int is_write;
+	grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
+} __attribute__((__packed__));
+
+struct sndif_request_common {
+	unsigned int dev_num;
+	unsigned int _pad2;
+	unsigned int dev_type;
+	unsigned int _pad3;
+	uint64_t     id;           /* private guest value, echoed in resp  */
+	unsigned int _pad4;
+	unsigned int _pad5;
+} __attribute__((__packed__));
+
+struct sndif_request {
+	uint8_t		operation;    /* SNDIF_OP_??? */
+	union {
+		struct sndif_request_open open;
+		struct sndif_request_ioctl ioctl;
+		struct sndif_request_rw rw;
+		struct sndif_request_common common;
+	} 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..7364679 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -24,6 +24,15 @@ 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
+	help
+	  This driver implements the back-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..c7f8827
--- /dev/null
+++ b/sound/drivers/xen-sndfront.c
@@ -0,0 +1,1117 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#include <sound/asound.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>
+
+#define		VSND_MAJOR		160
+#define		VSND_MINOR_CTRL		0
+#define		VSND_MINOR_STREAM	32
+
+#define		VSND_WAIT_ANSWER_TOUT	5000
+
+enum sndif_state {
+	SNDIF_STATE_DISCONNECTED,
+	SNDIF_STATE_CONNECTED,
+	SNDIF_STATE_SUSPENDED,
+};
+
+struct vsnd_stream {
+	dev_t dev;
+	struct vsnd_card *card;
+	unsigned int sample_bits;
+	unsigned int channels;
+	unsigned int stream_p;
+};
+
+struct vsnd_ctrl {
+	dev_t dev;
+	struct vsnd_card *card;
+};
+
+struct vsnd_card {
+	struct sndfront_info *fr_info;
+	unsigned int card_num;
+	unsigned int dev_num;
+	unsigned int dev_type;
+	unsigned int dev_id;
+	struct cdev cdev;
+	struct vsnd_stream *vstream;
+	struct vsnd_ctrl *vctrl;
+	grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST];
+	unsigned char *buf;
+};
+
+#define SND_RING_SIZE __CONST_RING_SIZE(sndif, PAGE_SIZE)
+
+struct sndfront_info {
+	struct mutex mutex;		/* protect sndfront closing state */
+	struct mutex tmp_fops_mutex;	/* protect file operations */
+	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;
+	struct class *vsndcl;
+	int bret_code;
+	atomic_t file_refcnt;
+};
+
+static struct class *vsndclass;
+
+static const struct file_operations vsndcore_fops = {
+	.owner	= THIS_MODULE,
+};
+
+#define GRANT_INVALID_REF	0
+
+static unsigned long vmalloc_to_mfn(void *address)
+{
+	return pfn_to_mfn(vmalloc_to_pfn(address));
+}
+
+static char *vsound_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "vsnd/%s", dev_name(dev));
+}
+
+static inline
+struct snd_interval *pcm_param_to_interval(struct snd_pcm_hw_params *p, int n)
+{
+	return &p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+static inline int pcm_param_is_interval(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
+	       (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
+}
+
+static unsigned int pcm_param_get_int(struct snd_pcm_hw_params *p, int n)
+{
+	if (pcm_param_is_interval(n)) {
+		struct snd_interval *i = pcm_param_to_interval(p, n);
+
+		if (i->integer)
+			return i->max;
+		}
+	return 0;
+}
+
+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)
+{
+	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.dev_type = info->vcard->dev_type;
+	req->u.open.dev_num = info->vcard->dev_num;
+	req->u.open.card_num = info->vcard->card_num;
+	req->u.open.id = info->vcard->dev_id;
+
+	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.dev_type = info->vcard->dev_type;
+	req->u.open.dev_num = info->vcard->dev_num;
+	req->u.open.card_num = info->vcard->card_num;
+	req->u.open.id = info->vcard->dev_id;
+
+	info->ring.req_prod_pvt++;
+
+	flush_requests(info);
+	return 0;
+}
+
+static int sndif_queue_request_ioctl(struct sndfront_info *info,
+				     unsigned int cmd,
+				     unsigned int add_len_to,
+				     unsigned int add_len_from)
+{
+	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_IOCTL;
+	req->u.ioctl.dev_type = info->vcard->dev_type;
+	req->u.ioctl.dev_num = info->vcard->dev_num;
+	req->u.ioctl.card_num = info->vcard->card_num;
+	req->u.ioctl.id = info->vcard->dev_id;
+
+	req->u.ioctl.cmd = cmd;
+	req->u.ioctl.add_len_to = add_len_to;
+	req->u.ioctl.add_len_from = add_len_from;
+
+	gref = info->vcard->grefs;
+
+	for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+		req->u.ioctl.gref[i] = gref[i];
+
+	info->ring.req_prod_pvt++;
+
+	flush_requests(info);
+	return 0;
+}
+
+static int vsnd_file_open(struct inode *inode, struct file *file)
+{
+	struct sndfront_info *info;
+	struct vsnd_card *vcard;
+	unsigned long answer_tout;
+	int ret = 0;
+
+	vcard = container_of(inode->i_cdev, struct vsnd_card, cdev);
+	info = vcard->fr_info;
+
+	mutex_lock(&info->tmp_fops_mutex);
+	file->private_data = info;
+
+	if (atomic_inc_return(&info->file_refcnt) > 1)
+		goto end_fops_handler;
+
+	reinit_completion(&info->completion);
+
+	if (sndif_queue_request_open(info)) {
+		ret = -EIO;
+		goto end_fops_handler;
+	}
+
+	answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+	if (wait_for_completion_interruptible_timeout(&info->completion,
+						      answer_tout) <= 0) {
+		ret = -ETIMEDOUT;
+		goto end_fops_handler;
+	}
+
+	ret = info->bret_code;
+
+end_fops_handler:
+	mutex_unlock(&info->tmp_fops_mutex);
+	return ret;
+}
+
+static int vsnd_file_release(struct inode *inode, struct file *file)
+{
+	struct sndfront_info *info;
+	unsigned long answer_tout;
+	int ret = 0;
+
+	info = file->private_data;
+
+	if (!info)
+		return -EINVAL;
+
+	mutex_lock(&info->tmp_fops_mutex);
+	if (atomic_dec_return(&info->file_refcnt) > 0)
+		goto end_fops_handler;
+
+	reinit_completion(&info->completion);
+
+	if (sndif_queue_request_close(info)) {
+		ret = -EIO;
+		goto end_fops_handler;
+	}
+
+	answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+	if (wait_for_completion_interruptible_timeout(&info->completion,
+						      answer_tout) <= 0) {
+		ret = -ETIMEDOUT;
+		goto end_fops_handler;
+	}
+
+	ret = info->bret_code;
+
+end_fops_handler:
+	mutex_unlock(&info->tmp_fops_mutex);
+	return ret;
+}
+
+static
+long vsnd_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	unsigned char *shared_data;
+	struct sndfront_info *info;
+	int datalen = 0;
+	int ret = 0;
+	int ioctl_dir;
+	unsigned int add_len_to = 0;
+	unsigned int frames;
+	struct snd_pcm_hw_params *hw_params;
+	struct snd_xferi *esnd_xferi;
+	struct vsnd_stream *vstream;
+	struct snd_ctl_elem_list *elist = NULL;
+	unsigned int elist_cnt = 0;
+	unsigned int add_len_from = 0;
+	unsigned long answer_tout;
+
+	info = file->private_data;
+
+	if (!info)
+		return -EINVAL;
+
+	mutex_lock(&info->tmp_fops_mutex);
+
+	shared_data = info->vcard->buf;
+
+	ioctl_dir = (cmd >> _IOC_DIRSHIFT) & _IOC_DIRMASK;
+	datalen = (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK;
+
+	if (datalen > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) {
+		ret = -EFAULT;
+		goto end_fops_handler;
+	}
+
+	if (datalen && (ioctl_dir & _IOC_WRITE)) {
+		if (copy_from_user(shared_data, (void __user *)arg, datalen)) {
+			ret = -EIO;
+			goto end_fops_handler;
+		}
+		/* Wait data to be visible to the other end */
+		wmb();
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
+		esnd_xferi = (struct snd_xferi *)shared_data;
+		vstream = info->vcard->vstream;
+		frames = esnd_xferi->frames;
+		add_len_to = vstream->channels * vstream->sample_bits / 8;
+		add_len_to *= frames;
+		if (add_len_to >
+			PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) {
+			ret = -EFAULT;
+			goto end_fops_handler;
+		}
+
+		if (copy_from_user(shared_data + datalen,
+				   esnd_xferi->buf, add_len_to)) {
+			ret = -EIO;
+			goto end_fops_handler;
+		}
+		/* Wait data to be visible to the other end */
+		wmb();
+		break;
+
+	case SNDRV_CTL_IOCTL_ELEM_LIST:
+		elist = (struct snd_ctl_elem_list *)shared_data;
+		elist_cnt = elist->count;
+
+		if (!elist_cnt)
+			break;
+
+		add_len_from = elist_cnt * sizeof(struct snd_ctl_elem_id);
+
+		break;
+	}
+
+	if (add_len_from > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) {
+		ret = -EFAULT;
+		goto end_fops_handler;
+	}
+
+	reinit_completion(&info->completion);
+
+	if (sndif_queue_request_ioctl(info, cmd, add_len_to, 0)) {
+		ret = -EIO;
+		goto end_fops_handler;
+	}
+
+	answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+	if (wait_for_completion_interruptible_timeout(&info->completion,
+						      answer_tout) <= 0) {
+		ret = -ETIMEDOUT;
+		goto end_fops_handler;
+	}
+
+	ret = info->bret_code;
+
+	if (ret)
+		goto end_fops_handler;
+
+	if (datalen && (ioctl_dir & _IOC_READ)) {
+		/* Wait data to be available from the other end */
+		rmb();
+		if (copy_to_user((void __user *)arg, shared_data, datalen)) {
+			ret = -EIO;
+			goto end_fops_handler;
+		}
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_HW_PARAMS:
+		/* Get PCM hw parameters */
+		hw_params = (struct snd_pcm_hw_params *)shared_data;
+		vstream = info->vcard->vstream;
+		vstream->channels =
+			pcm_param_get_int(hw_params,
+					  SNDRV_PCM_HW_PARAM_CHANNELS);
+		vstream->sample_bits =
+			pcm_param_get_int(hw_params,
+					  SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+		break;
+
+	case SNDRV_CTL_IOCTL_ELEM_LIST:
+		/* copy extra data from additional pages */
+		if (!add_len_from)
+			break;
+
+		/* Wait data to be available from the other end */
+		rmb();
+		if (copy_to_user((void __user *)elist->pids,
+				 shared_data + datalen, add_len_from)) {
+			ret = -EIO;
+			goto end_fops_handler;
+		}
+		break;
+	}
+
+end_fops_handler:
+	mutex_unlock(&info->tmp_fops_mutex);
+	return ret;
+}
+
+static const struct file_operations vsnd_file_ops = {
+	.open       = vsnd_file_open,
+	.release    = vsnd_file_release,
+	.unlocked_ioctl = vsnd_file_ioctl,
+	.owner = THIS_MODULE,
+};
+
+static struct vsnd_ctrl *sndif_allocate_vctrl(struct sndfront_info *info)
+{
+	struct vsnd_ctrl *vctrl;
+	unsigned int card_num;
+	grant_ref_t gref_head;
+	unsigned long mfn;
+	int ref;
+	int i;
+
+	vctrl = kmalloc(sizeof(*vctrl), GFP_KERNEL);
+
+	if (!vctrl)
+		goto err_ret;
+
+	card_num = info->vcard->card_num;
+
+	vctrl->card = info->vcard;
+
+	vctrl->dev = MKDEV(VSND_MAJOR, VSND_MINOR_CTRL);
+	if (device_create(info->vsndcl, NULL, vctrl->dev, NULL,
+			  "controlC%u", card_num) == NULL)
+		goto err_free_vctrl;
+
+	cdev_init(&info->vcard->cdev, &vsnd_file_ops);
+
+	if (cdev_add(&info->vcard->cdev, vctrl->dev, 1) < 0)
+		goto err_vctrl_dev_destroy;
+
+	info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
+	if (!info->vcard->buf)
+		goto err_vctrl_cdev_del;
+
+	if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
+					  &gref_head))
+		goto err_vctrl_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 vctrl;
+
+err_vctrl_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_vctrl_cdev_del:
+	cdev_del(&info->vcard->cdev);
+err_vctrl_dev_destroy:
+	device_destroy(info->vsndcl, vctrl->dev);
+err_free_vctrl:
+	kfree(vctrl);
+err_ret:
+	return vctrl;
+}
+
+static void sndif_free_vctrl(struct sndfront_info *info)
+{
+	int i;
+	struct vsnd_card *vcard = info->vcard;
+	struct vsnd_ctrl *vctrl = vcard->vctrl;
+
+	if (vctrl) {
+		vfree(info->vcard->buf);
+		for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+			gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL);
+
+		cdev_del(&info->vcard->cdev);
+		device_destroy(info->vsndcl, vctrl->dev);
+		kfree(vctrl);
+	}
+}
+
+static struct vsnd_stream *sndif_allocate_vstream(struct sndfront_info *info)
+{
+	struct vsnd_stream *vstream;
+	unsigned int card_num;
+	unsigned int dev_num;
+	unsigned int dev_minor;
+	grant_ref_t gref_head;
+	unsigned long mfn;
+	int ref;
+	int i;
+
+	vstream = kmalloc(sizeof(*vstream), GFP_KERNEL);
+
+	if (!vstream)
+		goto err_ret;
+
+	card_num = info->vcard->card_num;
+	dev_num = info->vcard->dev_num;
+	vstream->card = info->vcard;
+
+	if (info->vcard->dev_type == SNDIF_DEV_TYPE_STREAM_PLAY)
+		vstream->stream_p = 1;
+	else
+		vstream->stream_p = 0;
+
+	/* set default parameters */
+	vstream->channels = 2;
+	vstream->sample_bits = 16;
+
+	dev_minor = VSND_MINOR_STREAM + dev_num + 20 * (!!vstream->stream_p);
+	vstream->dev = MKDEV(VSND_MAJOR, dev_minor);
+	if (device_create(info->vsndcl, NULL, vstream->dev, NULL,
+			  "pcmC%uD%u%c", card_num, dev_num,
+			  vstream->stream_p ? 'p' : 'c') == NULL)
+		goto err_free_vstream;
+
+	cdev_init(&info->vcard->cdev, &vsnd_file_ops);
+
+	if (cdev_add(&info->vcard->cdev, vstream->dev, 1) < 0)
+		goto err_vstream_dev_destroy;
+
+	info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
+	if (!info->vcard->buf)
+		goto err_vstream_cdev_del;
+
+	if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
+					  &gref_head))
+		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 vstream;
+
+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_vstream_cdev_del:
+	cdev_del(&info->vcard->cdev);
+err_vstream_dev_destroy:
+	device_destroy(info->vsndcl, vstream->dev);
+err_free_vstream:
+	kfree(vstream);
+err_ret:
+	return vstream;
+}
+
+static void sndif_free_vstream(struct sndfront_info *info)
+{
+	int i;
+	struct vsnd_card *vcard = info->vcard;
+	struct vsnd_stream *vstream = vcard->vstream;
+
+	if (vstream) {
+		vfree(info->vcard->buf);
+		for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
+			gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL);
+
+		cdev_del(&info->vcard->cdev);
+		device_destroy(info->vsndcl, vstream->dev);
+		kfree(vstream);
+	}
+}
+
+static int sndif_add_virt_devices(struct sndfront_info *info,
+				  unsigned int dev_type,
+				  unsigned int dev_num,
+				  unsigned int card_num,
+				  unsigned int dev_id)
+{
+	int ret;
+
+	struct vsnd_card *vcard;
+	struct vsnd_ctrl *vctrl = NULL;
+	struct vsnd_stream *vstream = NULL;
+
+	vcard = kmalloc(sizeof(*vcard), GFP_KERNEL);
+
+	if (!vcard)
+		return -ENOMEM;
+
+	info->vsndcl = vsndclass;
+
+	vcard->card_num = card_num;
+	vcard->dev_num = dev_num;
+	vcard->dev_type = dev_type;
+	vcard->dev_id = dev_id;
+	vcard->fr_info = info;
+
+	info->vcard = vcard;
+
+	switch (dev_type) {
+	case SNDIF_DEV_TYPE_CONTROL:
+		vctrl = sndif_allocate_vctrl(info);
+
+		if (!vctrl) {
+			ret = -ENOMEM;
+			goto err_free_vcard;
+		}
+		break;
+
+	case SNDIF_DEV_TYPE_STREAM_PLAY:
+	case SNDIF_DEV_TYPE_STREAM_CAPTURE:
+		vstream = sndif_allocate_vstream(info);
+
+		if (!vstream) {
+			ret = -ENOMEM;
+			goto err_free_vcard;
+		}
+		break;
+
+	default:
+		ret = -EFAULT;
+		goto err_free_vcard;
+	}
+
+	info->vcard->vctrl = vctrl;
+	info->vcard->vstream = vstream;
+
+	return ret;
+
+err_free_vcard:
+	kfree(info->vcard);
+	return ret;
+}
+
+static void sndif_cleanup_virt_devices(struct sndfront_info *info)
+{
+	if (info->vcard) {
+		sndif_free_vstream(info);
+		sndif_free_vctrl(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_IOCTL:
+			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;
+}
+
+/**
+ * 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);
+	mutex_init(&info->tmp_fops_mutex);
+	spin_lock_init(&info->io_lock);
+	init_completion(&info->completion);
+	info->xbdev = dev;
+	atomic_set(&info->file_refcnt, 0);
+
+	dev_set_drvdata(&dev->dev, info);
+
+	err = talk_to_sndback(dev, info);
+	if (err) {
+		kfree(info);
+		dev_set_drvdata(&dev->dev, NULL);
+		return err;
+	}
+
+	return 0;
+}
+
+/**
+ * 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 dev_type;
+	unsigned int dev_num;
+	unsigned int card_num;
+	unsigned int dev_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);
+
+	spin_lock_irq(&info->io_lock);
+	info->connected = SNDIF_STATE_CONNECTED;
+	spin_unlock_irq(&info->io_lock);
+
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_type", "%u",
+			    &dev_type, NULL);
+	if (err)
+		return;
+
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_num", "%u",
+			    &dev_num, NULL);
+	if (err)
+		return;
+
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "card_num", "%u",
+			    &card_num, NULL);
+	if (err)
+		return;
+
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u",
+			    &dev_id, NULL);
+	if (err)
+		return;
+
+	sndif_add_virt_devices(info, dev_type, dev_num, card_num, dev_id);
+}
+
+/**
+ * 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);
+
+	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 = register_chrdev(VSND_MAJOR, "vsnd", &vsndcore_fops);
+	if (ret < 0)
+		return ret;
+
+	vsndclass = class_create(THIS_MODULE, "vsnd");
+	if (!vsndclass) {
+		ret = -ENOMEM;
+		goto err_reg_chrdev;
+	}
+
+	vsndclass->devnode = vsound_devnode;
+
+	ret = xenbus_register_frontend(&xen_snd_driver);
+	if (ret < 0)
+		goto err_class_destroy;
+
+	return ret;
+
+err_class_destroy:
+	class_destroy(vsndclass);
+err_reg_chrdev:
+	unregister_chrdev(VSND_MAJOR, "vsnd");
+	return ret;
+}
+
+static void __exit xen_snd_front_cleanup(void)
+{
+	class_destroy(vsndclass);
+	unregister_chrdev(VSND_MAJOR, "vsnd");
+	xenbus_unregister_driver(&xen_snd_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] 10+ messages in thread

* [PATCH 2/3] xen-sndfront: switch to the v2 driver
  2015-01-19  8:19 [PATCH 0/3] Add sound frontend driver Oleksandr Dmytryshyn
  2015-01-19  8:19 ` [PATCH 1/3] xen-sndfront: add " Oleksandr Dmytryshyn
@ 2015-01-19  8:19 ` Oleksandr Dmytryshyn
  2015-01-19 10:57   ` David Vrabel
  2015-01-19  8:19 ` [PATCH 3/3] xen-sndfront: add capture support Oleksandr Dmytryshyn
  2015-01-19 10:50 ` [PATCH 0/3] Add sound frontend driver David Vrabel
  3 siblings, 1 reply; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19  8:19 UTC (permalink / raw)
  To: xen-devel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela

From: Iurii Konovalenko <iurii.konovalenko@globallogic.com>

Now this 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: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@globallogic.com>
---
 include/xen/interface/io/sndif.h |   41 +-
 sound/drivers/Kconfig            |    3 +-
 sound/drivers/xen-sndfront.c     | 1362 ++++++++++++++++++++++++--------------
 3 files changed, 873 insertions(+), 533 deletions(-)

diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h
index 9ef0c41..2fae4df 100644
--- a/include/xen/interface/io/sndif.h
+++ b/include/xen/interface/io/sndif.h
@@ -19,10 +19,6 @@
 #define SNDIF_OP_WRITE			3
 #define SNDIF_OP_IOCTL			4
 
-#define SNDIF_DEV_TYPE_CONTROL		0
-#define SNDIF_DEV_TYPE_STREAM_PLAY	1
-#define SNDIF_DEV_TYPE_STREAM_CAPTURE	2
-
 #define SNDIF_MAX_PAGES_PER_REQUEST	10
 
 #define SNDIF_DEV_ID_CNT		5
@@ -35,53 +31,38 @@
  /* Operation completed successfully. */
 #define SNDIF_RSP_OKAY         0
 
+struct alsa_hwparams {
+	snd_pcm_format_t format;
+	unsigned int channels;
+	unsigned int rate;
+};
+
 struct sndif_request_open {
-	unsigned int dev_num;
-	unsigned int card_num;
-	unsigned int dev_type;
+	struct alsa_hwparams hwparams;
 	unsigned int _pad1;
 	uint64_t     id;           /* private guest value, echoed in resp  */
 	unsigned int _pad2;
-	unsigned int _pad3;
-} __attribute__((__packed__));
-
-struct sndif_request_ioctl {
-	unsigned int dev_num;
-	unsigned int card_num;
-	unsigned int dev_type;
-	unsigned int cmd;
-	uint64_t     id;           /* private guest value, echoed in resp  */
-	unsigned int add_len_to;
-	unsigned int add_len_from;
-	grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
 } __attribute__((__packed__));
 
 struct sndif_request_rw {
-	unsigned int dev_num;
-	unsigned int card_num;
-	unsigned int dev_type;
-	unsigned int _pad1;
+	struct alsa_hwparams _pad1;
+	unsigned int _pad2;
 	uint64_t     id;           /* private guest value, echoed in resp  */
 	unsigned int len;
-	unsigned int is_write;
 	grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
 } __attribute__((__packed__));
 
 struct sndif_request_common {
-	unsigned int dev_num;
+	struct alsa_hwparams _pad1;
 	unsigned int _pad2;
-	unsigned int dev_type;
-	unsigned int _pad3;
 	uint64_t     id;           /* private guest value, echoed in resp  */
-	unsigned int _pad4;
-	unsigned int _pad5;
+	unsigned int _pad3;
 } __attribute__((__packed__));
 
 struct sndif_request {
 	uint8_t		operation;    /* SNDIF_OP_??? */
 	union {
 		struct sndif_request_open open;
-		struct sndif_request_ioctl ioctl;
 		struct sndif_request_rw rw;
 		struct sndif_request_common common;
 	} u;
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 7364679..cd3db5a 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -28,8 +28,9 @@ 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 back-end of the Xen virtual
+	  This driver implements the front-end of the Xen virtual
 	  audio driver.  It communicates with a back-end
 	  in another domain.
 
diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
index c7f8827..da048fc 100644
--- a/sound/drivers/xen-sndfront.c
+++ b/sound/drivers/xen-sndfront.c
@@ -23,16 +23,26 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
+#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/errno.h>
-#include <linux/cdev.h>
-#include <linux/fs.h>
-#include <linux/stddef.h>
 #include <linux/vmalloc.h>
-#include <linux/delay.h>
-#include <linux/uaccess.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 <sound/asound.h>
+#include <linux/uaccess.h>
 
 #include <xen/xen.h>
 #include <xen/events.h>
@@ -44,101 +54,168 @@
 #include <xen/interface/io/protocols.h>
 #include <xen/interface/io/sndif.h>
 
-#define		VSND_MAJOR		160
-#define		VSND_MINOR_CTRL		0
-#define		VSND_MINOR_STREAM	32
-
-#define		VSND_WAIT_ANSWER_TOUT	5000
-
-enum sndif_state {
-	SNDIF_STATE_DISCONNECTED,
-	SNDIF_STATE_CONNECTED,
-	SNDIF_STATE_SUSPENDED,
-};
-
-struct vsnd_stream {
-	dev_t dev;
-	struct vsnd_card *card;
-	unsigned int sample_bits;
-	unsigned int channels;
-	unsigned int stream_p;
-};
-
-struct vsnd_ctrl {
-	dev_t dev;
-	struct vsnd_card *card;
-};
+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 card_num;
-	unsigned int dev_num;
-	unsigned int dev_type;
 	unsigned int dev_id;
-	struct cdev cdev;
-	struct vsnd_stream *vstream;
-	struct vsnd_ctrl *vctrl;
 	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 mutex tmp_fops_mutex;	/* protect file operations */
+	struct mutex mutex;
 	struct completion completion;
-	spinlock_t io_lock;		/* protect 'connected' member */
+	spinlock_t io_lock;
 	struct xenbus_device *xbdev;
 	enum sndif_state connected;
 	int ring_ref;
 	struct sndif_front_ring ring;
 	unsigned int evtchn, irq;
 	struct vsnd_card *vcard;
-	struct class *vsndcl;
 	int bret_code;
-	atomic_t file_refcnt;
+	struct platform_device *card_dev;
 };
 
-static struct class *vsndclass;
+#define GRANT_INVALID_REF	0
 
-static const struct file_operations vsndcore_fops = {
-	.owner	= THIS_MODULE,
+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;
 };
 
-#define GRANT_INVALID_REF	0
+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 */
+};
 
-static unsigned long vmalloc_to_mfn(void *address)
-{
-	return pfn_to_mfn(vmalloc_to_pfn(address));
-}
+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];
+};
 
-static char *vsound_devnode(struct device *dev, umode_t *mode)
-{
-	return kasprintf(GFP_KERNEL, "vsnd/%s", dev_name(dev));
-}
+/*
+ * 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 snd_interval *pcm_param_to_interval(struct snd_pcm_hw_params *p, int n)
-{
-	return &p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+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 inline int pcm_param_is_interval(int p)
-{
-	return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
-	       (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
-}
 
-static unsigned int pcm_param_get_int(struct snd_pcm_hw_params *p, int n)
+static unsigned long vmalloc_to_mfn(void *address)
 {
-	if (pcm_param_is_interval(n)) {
-		struct snd_interval *i = pcm_param_to_interval(p, n);
-
-		if (i->integer)
-			return i->max;
-		}
-	return 0;
+	return pfn_to_mfn(vmalloc_to_pfn(address));
 }
 
 static inline void flush_requests(struct sndfront_info *info)
@@ -151,7 +228,10 @@ static inline void flush_requests(struct sndfront_info *info)
 		notify_remote_via_irq(info->irq);
 }
 
-static int sndif_queue_request_open(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)
 {
 	struct sndif_request *req;
 
@@ -161,11 +241,10 @@ static int sndif_queue_request_open(struct sndfront_info *info)
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
 	req->operation = SNDIF_OP_OPEN;
-	req->u.open.dev_type = info->vcard->dev_type;
-	req->u.open.dev_num = info->vcard->dev_num;
-	req->u.open.card_num = info->vcard->card_num;
 	req->u.open.id = info->vcard->dev_id;
-
+	req->u.open.hwparams.format = format;
+	req->u.open.hwparams.channels = channels;
+	req->u.open.hwparams.rate = rate;
 	info->ring.req_prod_pvt++;
 
 	flush_requests(info);
@@ -182,9 +261,6 @@ static int sndif_queue_request_close(struct sndfront_info *info)
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
 	req->operation = SNDIF_OP_CLOSE;
-	req->u.open.dev_type = info->vcard->dev_type;
-	req->u.open.dev_num = info->vcard->dev_num;
-	req->u.open.card_num = info->vcard->card_num;
 	req->u.open.id = info->vcard->dev_id;
 
 	info->ring.req_prod_pvt++;
@@ -193,10 +269,8 @@ static int sndif_queue_request_close(struct sndfront_info *info)
 	return 0;
 }
 
-static int sndif_queue_request_ioctl(struct sndfront_info *info,
-				     unsigned int cmd,
-				     unsigned int add_len_to,
-				     unsigned int add_len_from)
+static int sndif_queue_request_write(struct sndfront_info *info,
+				     unsigned int len)
 {
 	struct sndif_request *req;
 	grant_ref_t *gref;
@@ -207,20 +281,15 @@ static int sndif_queue_request_ioctl(struct sndfront_info *info,
 
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
-	req->operation = SNDIF_OP_IOCTL;
-	req->u.ioctl.dev_type = info->vcard->dev_type;
-	req->u.ioctl.dev_num = info->vcard->dev_num;
-	req->u.ioctl.card_num = info->vcard->card_num;
-	req->u.ioctl.id = info->vcard->dev_id;
+	req->operation = SNDIF_OP_WRITE;
+	req->u.rw.id = info->vcard->dev_id;
 
-	req->u.ioctl.cmd = cmd;
-	req->u.ioctl.add_len_to = add_len_to;
-	req->u.ioctl.add_len_from = add_len_from;
+	req->u.rw.len = len;
 
 	gref = info->vcard->grefs;
 
 	for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
-		req->u.ioctl.gref[i] = gref[i];
+		req->u.rw.gref[i] = gref[i];
 
 	info->ring.req_prod_pvt++;
 
@@ -228,361 +297,112 @@ static int sndif_queue_request_ioctl(struct sndfront_info *info,
 	return 0;
 }
 
-static int vsnd_file_open(struct inode *inode, struct file *file)
+static int alsa_pcm_open(struct sndfront_info *info,
+			 snd_pcm_format_t format,
+			 unsigned int channels,
+			 unsigned int rate)
 {
-	struct sndfront_info *info;
-	struct vsnd_card *vcard;
 	unsigned long answer_tout;
-	int ret = 0;
-
-	vcard = container_of(inode->i_cdev, struct vsnd_card, cdev);
-	info = vcard->fr_info;
-
-	mutex_lock(&info->tmp_fops_mutex);
-	file->private_data = info;
-
-	if (atomic_inc_return(&info->file_refcnt) > 1)
-		goto end_fops_handler;
 
 	reinit_completion(&info->completion);
 
-	if (sndif_queue_request_open(info)) {
-		ret = -EIO;
-		goto end_fops_handler;
-	}
+	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) {
-		ret = -ETIMEDOUT;
-		goto end_fops_handler;
-	}
-
-	ret = info->bret_code;
+						      answer_tout) <= 0)
+		return -ETIMEDOUT;
 
-end_fops_handler:
-	mutex_unlock(&info->tmp_fops_mutex);
-	return ret;
+	return info->bret_code;
 }
 
-static int vsnd_file_release(struct inode *inode, struct file *file)
+static int alsa_pcm_close(struct sndfront_info *info)
 {
-	struct sndfront_info *info;
 	unsigned long answer_tout;
-	int ret = 0;
-
-	info = file->private_data;
-
-	if (!info)
-		return -EINVAL;
-
-	mutex_lock(&info->tmp_fops_mutex);
-	if (atomic_dec_return(&info->file_refcnt) > 0)
-		goto end_fops_handler;
 
 	reinit_completion(&info->completion);
 
-	if (sndif_queue_request_close(info)) {
-		ret = -EIO;
-		goto end_fops_handler;
-	}
+	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) {
-		ret = -ETIMEDOUT;
-		goto end_fops_handler;
-	}
-
-	ret = info->bret_code;
+						      answer_tout) <= 0)
+		return -ETIMEDOUT;
 
-end_fops_handler:
-	mutex_unlock(&info->tmp_fops_mutex);
-	return ret;
+	return info->bret_code;
 }
 
-static
-long vsnd_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static int alsa_pcm_write(struct sndfront_info *info, char __user *buf,
+			  int len)
 {
 	unsigned char *shared_data;
-	struct sndfront_info *info;
-	int datalen = 0;
-	int ret = 0;
-	int ioctl_dir;
-	unsigned int add_len_to = 0;
-	unsigned int frames;
-	struct snd_pcm_hw_params *hw_params;
-	struct snd_xferi *esnd_xferi;
-	struct vsnd_stream *vstream;
-	struct snd_ctl_elem_list *elist = NULL;
-	unsigned int elist_cnt = 0;
-	unsigned int add_len_from = 0;
 	unsigned long answer_tout;
 
-	info = file->private_data;
-
-	if (!info)
-		return -EINVAL;
-
-	mutex_lock(&info->tmp_fops_mutex);
-
 	shared_data = info->vcard->buf;
 
-	ioctl_dir = (cmd >> _IOC_DIRSHIFT) & _IOC_DIRMASK;
-	datalen = (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK;
+	if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+		return -EFAULT;
 
-	if (datalen > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) {
-		ret = -EFAULT;
-		goto end_fops_handler;
-	}
-
-	if (datalen && (ioctl_dir & _IOC_WRITE)) {
-		if (copy_from_user(shared_data, (void __user *)arg, datalen)) {
-			ret = -EIO;
-			goto end_fops_handler;
-		}
-		/* Wait data to be visible to the other end */
-		wmb();
-	}
-
-	switch (cmd) {
-	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
-		esnd_xferi = (struct snd_xferi *)shared_data;
-		vstream = info->vcard->vstream;
-		frames = esnd_xferi->frames;
-		add_len_to = vstream->channels * vstream->sample_bits / 8;
-		add_len_to *= frames;
-		if (add_len_to >
-			PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) {
-			ret = -EFAULT;
-			goto end_fops_handler;
-		}
-
-		if (copy_from_user(shared_data + datalen,
-				   esnd_xferi->buf, add_len_to)) {
-			ret = -EIO;
-			goto end_fops_handler;
-		}
-		/* Wait data to be visible to the other end */
-		wmb();
-		break;
-
-	case SNDRV_CTL_IOCTL_ELEM_LIST:
-		elist = (struct snd_ctl_elem_list *)shared_data;
-		elist_cnt = elist->count;
-
-		if (!elist_cnt)
-			break;
-
-		add_len_from = elist_cnt * sizeof(struct snd_ctl_elem_id);
-
-		break;
-	}
-
-	if (add_len_from > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) {
-		ret = -EFAULT;
-		goto end_fops_handler;
-	}
+	if (copy_from_user(shared_data, buf, len))
+		return -EFAULT;
 
 	reinit_completion(&info->completion);
 
-	if (sndif_queue_request_ioctl(info, cmd, add_len_to, 0)) {
-		ret = -EIO;
-		goto end_fops_handler;
-	}
+	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) {
-		ret = -ETIMEDOUT;
-		goto end_fops_handler;
-	}
-
-	ret = info->bret_code;
-
-	if (ret)
-		goto end_fops_handler;
-
-	if (datalen && (ioctl_dir & _IOC_READ)) {
-		/* Wait data to be available from the other end */
-		rmb();
-		if (copy_to_user((void __user *)arg, shared_data, datalen)) {
-			ret = -EIO;
-			goto end_fops_handler;
-		}
-	}
-
-	switch (cmd) {
-	case SNDRV_PCM_IOCTL_HW_PARAMS:
-		/* Get PCM hw parameters */
-		hw_params = (struct snd_pcm_hw_params *)shared_data;
-		vstream = info->vcard->vstream;
-		vstream->channels =
-			pcm_param_get_int(hw_params,
-					  SNDRV_PCM_HW_PARAM_CHANNELS);
-		vstream->sample_bits =
-			pcm_param_get_int(hw_params,
-					  SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-		break;
-
-	case SNDRV_CTL_IOCTL_ELEM_LIST:
-		/* copy extra data from additional pages */
-		if (!add_len_from)
-			break;
+						      answer_tout) <= 0)
+		return -ETIMEDOUT;
 
-		/* Wait data to be available from the other end */
-		rmb();
-		if (copy_to_user((void __user *)elist->pids,
-				 shared_data + datalen, add_len_from)) {
-			ret = -EIO;
-			goto end_fops_handler;
-		}
-		break;
-	}
-
-end_fops_handler:
-	mutex_unlock(&info->tmp_fops_mutex);
-	return ret;
+	return info->bret_code;
 }
 
-static const struct file_operations vsnd_file_ops = {
-	.open       = vsnd_file_open,
-	.release    = vsnd_file_release,
-	.unlocked_ioctl = vsnd_file_ioctl,
-	.owner = THIS_MODULE,
-};
-
-static struct vsnd_ctrl *sndif_allocate_vctrl(struct sndfront_info *info)
+static int alsa_pcm_silence(struct sndfront_info *info, int len)
 {
-	struct vsnd_ctrl *vctrl;
-	unsigned int card_num;
-	grant_ref_t gref_head;
-	unsigned long mfn;
-	int ref;
-	int i;
-
-	vctrl = kmalloc(sizeof(*vctrl), GFP_KERNEL);
-
-	if (!vctrl)
-		goto err_ret;
-
-	card_num = info->vcard->card_num;
-
-	vctrl->card = info->vcard;
-
-	vctrl->dev = MKDEV(VSND_MAJOR, VSND_MINOR_CTRL);
-	if (device_create(info->vsndcl, NULL, vctrl->dev, NULL,
-			  "controlC%u", card_num) == NULL)
-		goto err_free_vctrl;
-
-	cdev_init(&info->vcard->cdev, &vsnd_file_ops);
-
-	if (cdev_add(&info->vcard->cdev, vctrl->dev, 1) < 0)
-		goto err_vctrl_dev_destroy;
-
-	info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
-	if (!info->vcard->buf)
-		goto err_vctrl_cdev_del;
-
-	if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
-					  &gref_head))
-		goto err_vctrl_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);
+	unsigned char *shared_data;
+	unsigned long answer_tout;
 
-		gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
-						mfn, 0);
+	shared_data = info->vcard->buf;
 
-		info->vcard->grefs[i] = ref;
-	}
+	if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+		return -EFAULT;
 
-	gnttab_free_grant_references(gref_head);
-	return vctrl;
+	memset(shared_data, 0, len);
 
-err_vctrl_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);
+	reinit_completion(&info->completion);
 
-	gnttab_free_grant_references(gref_head);
-err_vctrl_cdev_del:
-	cdev_del(&info->vcard->cdev);
-err_vctrl_dev_destroy:
-	device_destroy(info->vsndcl, vctrl->dev);
-err_free_vctrl:
-	kfree(vctrl);
-err_ret:
-	return vctrl;
-}
+	if (sndif_queue_request_write(info, len))
+		return -EIO;
 
-static void sndif_free_vctrl(struct sndfront_info *info)
-{
-	int i;
-	struct vsnd_card *vcard = info->vcard;
-	struct vsnd_ctrl *vctrl = vcard->vctrl;
-
-	if (vctrl) {
-		vfree(info->vcard->buf);
-		for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
-			gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL);
+	answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
+	if (wait_for_completion_interruptible_timeout(&info->completion,
+						      answer_tout) <= 0)
+		return -ETIMEDOUT;
 
-		cdev_del(&info->vcard->cdev);
-		device_destroy(info->vsndcl, vctrl->dev);
-		kfree(vctrl);
-	}
+	return info->bret_code;
 }
 
-static struct vsnd_stream *sndif_allocate_vstream(struct sndfront_info *info)
+static int sndif_setup_vcard(struct sndfront_info *info)
 {
-	struct vsnd_stream *vstream;
-	unsigned int card_num;
-	unsigned int dev_num;
-	unsigned int dev_minor;
 	grant_ref_t gref_head;
 	unsigned long mfn;
 	int ref;
 	int i;
-
-	vstream = kmalloc(sizeof(*vstream), GFP_KERNEL);
-
-	if (!vstream)
-		goto err_ret;
-
-	card_num = info->vcard->card_num;
-	dev_num = info->vcard->dev_num;
-	vstream->card = info->vcard;
-
-	if (info->vcard->dev_type == SNDIF_DEV_TYPE_STREAM_PLAY)
-		vstream->stream_p = 1;
-	else
-		vstream->stream_p = 0;
-
-	/* set default parameters */
-	vstream->channels = 2;
-	vstream->sample_bits = 16;
-
-	dev_minor = VSND_MINOR_STREAM + dev_num + 20 * (!!vstream->stream_p);
-	vstream->dev = MKDEV(VSND_MAJOR, dev_minor);
-	if (device_create(info->vsndcl, NULL, vstream->dev, NULL,
-			  "pcmC%uD%u%c", card_num, dev_num,
-			  vstream->stream_p ? 'p' : 'c') == NULL)
-		goto err_free_vstream;
-
-	cdev_init(&info->vcard->cdev, &vsnd_file_ops);
-
-	if (cdev_add(&info->vcard->cdev, vstream->dev, 1) < 0)
-		goto err_vstream_dev_destroy;
+	int ret;
 
 	info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
-	if (!info->vcard->buf)
-		goto err_vstream_cdev_del;
+	if (!info->vcard->buf) {
+		ret = -ENOMEM;
+		goto err_ret;
+	}
 
-	if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
-					  &gref_head))
+	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++) {
@@ -598,7 +418,7 @@ static struct vsnd_stream *sndif_allocate_vstream(struct sndfront_info *info)
 	}
 
 	gnttab_free_grant_references(gref_head);
-	return vstream;
+	return ret;
 
 err_vstream_free_buf:
 	vfree(info->vcard->buf);
@@ -606,100 +426,48 @@ err_vstream_free_buf:
 		gnttab_end_foreign_access(info->vcard->grefs[i], 0, 0UL);
 
 	gnttab_free_grant_references(gref_head);
-err_vstream_cdev_del:
-	cdev_del(&info->vcard->cdev);
-err_vstream_dev_destroy:
-	device_destroy(info->vsndcl, vstream->dev);
-err_free_vstream:
-	kfree(vstream);
 err_ret:
-	return vstream;
+	return ret;
 }
 
-static void sndif_free_vstream(struct sndfront_info *info)
+static void sndif_cleanup_vcard(struct sndfront_info *info)
 {
 	int i;
-	struct vsnd_card *vcard = info->vcard;
-	struct vsnd_stream *vstream = vcard->vstream;
 
-	if (vstream) {
+	if (info->vcard->buf) {
 		vfree(info->vcard->buf);
 		for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
-			gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL);
-
-		cdev_del(&info->vcard->cdev);
-		device_destroy(info->vsndcl, vstream->dev);
-		kfree(vstream);
+			gnttab_end_foreign_access(info->vcard->grefs[i],
+						  0, 0UL);
 	}
 }
 
 static int sndif_add_virt_devices(struct sndfront_info *info,
-				  unsigned int dev_type,
-				  unsigned int dev_num,
-				  unsigned int card_num,
 				  unsigned int dev_id)
 {
-	int ret;
+	int ret = 0;
 
 	struct vsnd_card *vcard;
-	struct vsnd_ctrl *vctrl = NULL;
-	struct vsnd_stream *vstream = NULL;
 
 	vcard = kmalloc(sizeof(*vcard), GFP_KERNEL);
 
 	if (!vcard)
 		return -ENOMEM;
 
-	info->vsndcl = vsndclass;
-
-	vcard->card_num = card_num;
-	vcard->dev_num = dev_num;
-	vcard->dev_type = dev_type;
 	vcard->dev_id = dev_id;
 	vcard->fr_info = info;
 
 	info->vcard = vcard;
 
-	switch (dev_type) {
-	case SNDIF_DEV_TYPE_CONTROL:
-		vctrl = sndif_allocate_vctrl(info);
-
-		if (!vctrl) {
-			ret = -ENOMEM;
-			goto err_free_vcard;
-		}
-		break;
-
-	case SNDIF_DEV_TYPE_STREAM_PLAY:
-	case SNDIF_DEV_TYPE_STREAM_CAPTURE:
-		vstream = sndif_allocate_vstream(info);
-
-		if (!vstream) {
-			ret = -ENOMEM;
-			goto err_free_vcard;
-		}
-		break;
-
-	default:
-		ret = -EFAULT;
-		goto err_free_vcard;
-	}
+	sndif_setup_vcard(info);
 
-	info->vcard->vctrl = vctrl;
-	info->vcard->vstream = vstream;
-
-	return ret;
-
-err_free_vcard:
-	kfree(info->vcard);
 	return ret;
 }
 
 static void sndif_cleanup_virt_devices(struct sndfront_info *info)
 {
 	if (info->vcard) {
-		sndif_free_vstream(info);
-		sndif_free_vctrl(info);
+		sndif_cleanup_vcard(info);
 		kfree(info->vcard);
 	}
 }
@@ -748,7 +516,7 @@ static irqreturn_t sndif_interrupt(int irq, void *data)
 		switch (bret->operation) {
 		case SNDIF_OP_OPEN:
 		case SNDIF_OP_CLOSE:
-		case SNDIF_OP_IOCTL:
+		case SNDIF_OP_WRITE:
 			if (unlikely(bret->status != SNDIF_RSP_OKAY))
 				dev_dbg(&info->xbdev->dev,
 					"snddev data request error: %x\n",
@@ -877,6 +645,620 @@ again:
 	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
@@ -896,22 +1278,29 @@ static int sndfront_probe(struct xenbus_device *dev,
 	}
 
 	mutex_init(&info->mutex);
-	mutex_init(&info->tmp_fops_mutex);
 	spin_lock_init(&info->io_lock);
 	init_completion(&info->completion);
 	info->xbdev = dev;
-	atomic_set(&info->file_refcnt, 0);
 
 	dev_set_drvdata(&dev->dev, info);
 
 	err = talk_to_sndback(dev, info);
-	if (err) {
-		kfree(info);
-		dev_set_drvdata(&dev->dev, NULL);
-		return err;
+	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;
 }
 
 /**
@@ -960,9 +1349,6 @@ sndfront_closing(struct sndfront_info *info)
  */
 static void sndfront_connect(struct sndfront_info *info)
 {
-	unsigned int dev_type;
-	unsigned int dev_num;
-	unsigned int card_num;
 	unsigned int dev_id;
 	int err;
 
@@ -982,31 +1368,18 @@ static void sndfront_connect(struct sndfront_info *info)
 
 	xenbus_switch_state(info->xbdev, XenbusStateConnected);
 
-	spin_lock_irq(&info->io_lock);
-	info->connected = SNDIF_STATE_CONNECTED;
-	spin_unlock_irq(&info->io_lock);
-
-	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_type", "%u",
-			    &dev_type, NULL);
-	if (err)
-		return;
-
-	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_num", "%u",
-			    &dev_num, NULL);
-	if (err)
-		return;
-
-	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "card_num", "%u",
-			    &card_num, NULL);
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u",
+			    &dev_id, NULL);
 	if (err)
 		return;
 
-	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u",
-			    &dev_id, NULL);
+	err = sndif_add_virt_devices(info, dev_id);
 	if (err)
 		return;
 
-	sndif_add_virt_devices(info, dev_type, dev_num, card_num, dev_id);
+	spin_lock_irq(&info->io_lock);
+	info->connected = SNDIF_STATE_CONNECTED;
+	spin_unlock_irq(&info->io_lock);
 }
 
 /**
@@ -1046,6 +1419,8 @@ static int sndfront_remove(struct xenbus_device *xbdev)
 
 	dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
 
+	platform_device_unregister(info->card_dev);
+
 	sndif_free(info, 0);
 
 	return 0;
@@ -1077,36 +1452,19 @@ static int __init xen_snd_front_init(void)
 	if (xen_initial_domain())
 		return -ENODEV;
 
-	ret = register_chrdev(VSND_MAJOR, "vsnd", &vsndcore_fops);
-	if (ret < 0)
-		return ret;
-
-	vsndclass = class_create(THIS_MODULE, "vsnd");
-	if (!vsndclass) {
-		ret = -ENOMEM;
-		goto err_reg_chrdev;
-	}
-
-	vsndclass->devnode = vsound_devnode;
-
 	ret = xenbus_register_frontend(&xen_snd_driver);
-	if (ret < 0)
-		goto err_class_destroy;
+	if (ret)
+		return ret;
 
-	return ret;
+	ret = platform_driver_register(&snd_virtualcard_driver);
 
-err_class_destroy:
-	class_destroy(vsndclass);
-err_reg_chrdev:
-	unregister_chrdev(VSND_MAJOR, "vsnd");
 	return ret;
 }
 
 static void __exit xen_snd_front_cleanup(void)
 {
-	class_destroy(vsndclass);
-	unregister_chrdev(VSND_MAJOR, "vsnd");
 	xenbus_unregister_driver(&xen_snd_driver);
+	platform_driver_unregister(&snd_virtualcard_driver);
 }
 
 module_init(xen_snd_front_init);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/3] xen-sndfront: add capture support
  2015-01-19  8:19 [PATCH 0/3] Add sound frontend driver Oleksandr Dmytryshyn
  2015-01-19  8:19 ` [PATCH 1/3] xen-sndfront: add " Oleksandr Dmytryshyn
  2015-01-19  8:19 ` [PATCH 2/3] xen-sndfront: switch to the v2 driver Oleksandr Dmytryshyn
@ 2015-01-19  8:19 ` Oleksandr Dmytryshyn
  2015-01-19 10:50 ` [PATCH 0/3] Add sound frontend driver David Vrabel
  3 siblings, 0 replies; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19  8:19 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>
---
 include/xen/interface/io/sndif.h |  93 +++++++++++++---
 sound/drivers/xen-sndfront.c     | 222 ++++++++++++++++++++++++++++++---------
 2 files changed, 252 insertions(+), 63 deletions(-)

diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h
index 2fae4df..dafd90b 100644
--- a/include/xen/interface/io/sndif.h
+++ b/include/xen/interface/io/sndif.h
@@ -11,17 +11,70 @@
 #include <xen/interface/grant_table.h>
 
 /*
+ * 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_OP_IOCTL			4
+#define SNDIF_SET_VOLUME		4
+#define SNDIF_GET_VOLUME		5
 
 #define SNDIF_MAX_PAGES_PER_REQUEST	10
 
-#define SNDIF_DEV_ID_CNT		5
+#define SNDIF_DEV_ID_CNT		2
 
 /*
  * STATUS RETURN CODES.
@@ -32,39 +85,47 @@
 #define SNDIF_RSP_OKAY         0
 
 struct alsa_hwparams {
-	snd_pcm_format_t format;
-	unsigned int channels;
-	unsigned int rate;
+	uint32_t format;
+	uint32_t channels;
+	uint32_t rate;
 };
 
+struct sndif_request_common {
+	uint64_t     id;           /* private guest value, echoed in resp  */
+	struct alsa_hwparams _pad1;
+	uint32_t _pad2;
+	uint32_t _pad3;
+} __attribute__((__packed__));
+
 struct sndif_request_open {
-	struct alsa_hwparams hwparams;
-	unsigned int _pad1;
 	uint64_t     id;           /* private guest value, echoed in resp  */
-	unsigned int _pad2;
+	struct alsa_hwparams hwparams;
+	uint32_t stream;
+	uint32_t _pad2;
 } __attribute__((__packed__));
 
 struct sndif_request_rw {
-	struct alsa_hwparams _pad1;
-	unsigned int _pad2;
 	uint64_t     id;           /* private guest value, echoed in resp  */
-	unsigned int len;
+	struct alsa_hwparams _pad1;
+	uint32_t len;
+	uint32_t _pad2;
 	grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
 } __attribute__((__packed__));
 
-struct sndif_request_common {
-	struct alsa_hwparams _pad1;
-	unsigned int _pad2;
+struct sndif_request_volume {
 	uint64_t     id;           /* private guest value, echoed in resp  */
-	unsigned int _pad3;
+	struct alsa_hwparams _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_common common;
+		struct sndif_request_volume vol;
 	} u;
 } __attribute__((__packed__));
 
diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
index da048fc..4bb02ec 100644
--- a/sound/drivers/xen-sndfront.c
+++ b/sound/drivers/xen-sndfront.c
@@ -95,8 +95,7 @@ MODULE_PARM_DESC(model, "Soundcard model.");
 #define MIXER_ADDR_LAST			MIXER_ADDR_MASTER_OUT
 
 struct vsnd_card {
-	struct sndfront_info *fr_info;
-	unsigned int dev_id;
+	unsigned int stream_id;
 	grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST];
 	unsigned char *buf;
 };
@@ -120,7 +119,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
@@ -157,7 +162,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];
 };
 
@@ -203,15 +207,33 @@ 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) {
+				     struct snd_pcm_substream *substream)
+{
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return &virtualcard->streams[0];
 	else
 		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)
 {
@@ -231,7 +253,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;
 
@@ -241,10 +264,11 @@ static int sndif_queue_request_open(struct sndfront_info *info,
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
 	req->operation = SNDIF_OP_OPEN;
-	req->u.open.id = info->vcard->dev_id;
+	req->u.open.id = info->vcard->stream_id;
 	req->u.open.hwparams.format = format;
 	req->u.open.hwparams.channels = channels;
 	req->u.open.hwparams.rate = rate;
+	req->u.open.stream = stream;
 	info->ring.req_prod_pvt++;
 
 	flush_requests(info);
@@ -261,7 +285,7 @@ static int sndif_queue_request_close(struct sndfront_info *info)
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
 	req->operation = SNDIF_OP_CLOSE;
-	req->u.open.id = info->vcard->dev_id;
+	req->u.open.id = info->vcard->stream_id;
 
 	info->ring.req_prod_pvt++;
 
@@ -282,7 +306,35 @@ static int sndif_queue_request_write(struct sndfront_info *info,
 	req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
 
 	req->operation = SNDIF_OP_WRITE;
-	req->u.rw.id = info->vcard->dev_id;
+	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 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;
 
@@ -300,13 +352,17 @@ static int sndif_queue_request_write(struct sndfront_info *info,
 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);
@@ -321,6 +377,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))
@@ -340,6 +399,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)
@@ -361,6 +423,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;
@@ -443,7 +534,7 @@ static void sndif_cleanup_vcard(struct sndfront_info *info)
 }
 
 static int sndif_add_virt_devices(struct sndfront_info *info,
-				  unsigned int dev_id)
+				  unsigned int stream_id)
 {
 	int ret = 0;
 
@@ -454,8 +545,7 @@ static int sndif_add_virt_devices(struct sndfront_info *info,
 	if (!vcard)
 		return -ENOMEM;
 
-	vcard->dev_id = dev_id;
-	vcard->fr_info = info;
+	vcard->stream_id = stream_id;
 
 	info->vcard = vcard;
 
@@ -517,6 +607,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",
@@ -674,16 +765,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;
 
@@ -696,26 +790,36 @@ 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;
-	struct stream_info *vcard_stream;
 
-	vcard_stream = get_vcard_stream(virtualcard, substream);
-
-	if (vcard_stream->crossed) {
+	if (virtualcard->streams[0].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;
+		virtualcard->streams[0].crossed = 0;
 	}
-	return vcard_stream->position;
+	return virtualcard->streams[0].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;
+
+#ifdef MAX_CAPTURE_SIZE
+	res = (virtualcard->streams[1].position + MAX_CAPTURE_SIZE) % buff_size;
+#else
+	res = (virtualcard->streams[1].position + buff_size / 2) % buff_size;
+#endif
+	return res;
 }
 
 static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -760,8 +864,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;
 
@@ -779,11 +884,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,
@@ -794,12 +899,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,
@@ -811,11 +916,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 = {
@@ -826,7 +932,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,
 };
@@ -839,7 +945,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,
 };
 
@@ -1157,8 +1263,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];
 
@@ -1268,7 +1372,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);
@@ -1288,12 +1392,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;
 
@@ -1349,7 +1467,7 @@ sndfront_closing(struct sndfront_info *info)
  */
 static void sndfront_connect(struct sndfront_info *info)
 {
-	unsigned int dev_id;
+	unsigned int stream_id;
 	int err;
 
 	switch (info->connected) {
@@ -1368,12 +1486,12 @@ static void sndfront_connect(struct sndfront_info *info)
 
 	xenbus_switch_state(info->xbdev, XenbusStateConnected);
 
-	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u",
-			    &dev_id, NULL);
+	err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "stream_id", "%u",
+			    &stream_id, NULL);
 	if (err)
 		return;
 
-	err = sndif_add_virt_devices(info, dev_id);
+	err = sndif_add_virt_devices(info, stream_id);
 	if (err)
 		return;
 
@@ -1415,11 +1533,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);
 
@@ -1444,6 +1570,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] 10+ messages in thread

* Re: [PATCH 0/3] Add sound frontend driver
  2015-01-19  8:19 [PATCH 0/3] Add sound frontend driver Oleksandr Dmytryshyn
                   ` (2 preceding siblings ...)
  2015-01-19  8:19 ` [PATCH 3/3] xen-sndfront: add capture support Oleksandr Dmytryshyn
@ 2015-01-19 10:50 ` David Vrabel
  2015-01-19 11:14   ` Oleksandr Dmytryshyn
  3 siblings, 1 reply; 10+ messages in thread
From: David Vrabel @ 2015-01-19 10:50 UTC (permalink / raw)
  To: Oleksandr Dmytryshyn, xen-devel
  Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela

On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
> 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.

> 
> Iurii Konovalenko (2):
>   xen-sndfront: switch to the v2 driver
>   xen-sndfront: add capture support
> 
> Oleksandr Dmytryshyn (1):
>   xen-sndfront: add sound frontend driver
> 
>  include/xen/interface/io/sndif.h |  140 ++++

This ABI needs to be properly defined and documented and accepted into
Xen before we can take any frontend drivers using it.

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] xen-sndfront: add sound frontend driver
  2015-01-19  8:19 ` [PATCH 1/3] xen-sndfront: add " Oleksandr Dmytryshyn
@ 2015-01-19 10:55   ` David Vrabel
  2015-01-19 11:13     ` Oleksandr Dmytryshyn
  0 siblings, 1 reply; 10+ messages in thread
From: David Vrabel @ 2015-01-19 10:55 UTC (permalink / raw)
  To: Oleksandr Dmytryshyn, xen-devel
  Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela

On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
> +
> +struct sndif_request_open {
> +	unsigned int dev_num;
> +	unsigned int card_num;
> +	unsigned int dev_type;
> +	unsigned int _pad1;
> +	uint64_t     id;           /* private guest value, echoed in resp  */
> +	unsigned int _pad2;
> +	unsigned int _pad3;
> +} __attribute__((__packed__));

Using types like unsigned int will break 32-bit guests talking to 64-bit
backends.

Always use fixed width types, naturally align them and use explicit
padding.  The packed attribute will be unnecessary.

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/3] xen-sndfront: switch to the v2 driver
  2015-01-19  8:19 ` [PATCH 2/3] xen-sndfront: switch to the v2 driver Oleksandr Dmytryshyn
@ 2015-01-19 10:57   ` David Vrabel
  2015-01-19 11:17     ` Oleksandr Dmytryshyn
  0 siblings, 1 reply; 10+ messages in thread
From: David Vrabel @ 2015-01-19 10:57 UTC (permalink / raw)
  To: Oleksandr Dmytryshyn, xen-devel
  Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela

On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
> From: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
> 
> Now this 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.
> 
[...]
>  struct sndif_request_open {
> -	unsigned int dev_num;
> -	unsigned int card_num;
> -	unsigned int dev_type;
> +	struct alsa_hwparams hwparams;

Don't put ALSA specific structures in the ABI.  The protocol needs to be
suitable for frontend drivers that aren't ALSA.

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] xen-sndfront: add sound frontend driver
  2015-01-19 10:55   ` David Vrabel
@ 2015-01-19 11:13     ` Oleksandr Dmytryshyn
  0 siblings, 0 replies; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19 11:13 UTC (permalink / raw)
  To: David Vrabel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela, xen-devel

On Mon, Jan 19, 2015 at 12:55 PM, David Vrabel <david.vrabel@citrix.com> wrote:
> On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
>> +
>> +struct sndif_request_open {
>> +     unsigned int dev_num;
>> +     unsigned int card_num;
>> +     unsigned int dev_type;
>> +     unsigned int _pad1;
>> +     uint64_t     id;           /* private guest value, echoed in resp  */
>> +     unsigned int _pad2;
>> +     unsigned int _pad3;
>> +} __attribute__((__packed__));
>
> Using types like unsigned int will break 32-bit guests talking to 64-bit
> backends.
I'll fix this in the next patch-set

> Always use fixed width types, naturally align them and use explicit
> padding.  The packed attribute will be unnecessary.
>
> David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/3] Add sound frontend driver
  2015-01-19 10:50 ` [PATCH 0/3] Add sound frontend driver David Vrabel
@ 2015-01-19 11:14   ` Oleksandr Dmytryshyn
  0 siblings, 0 replies; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19 11:14 UTC (permalink / raw)
  To: David Vrabel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela, xen-devel

On Mon, Jan 19, 2015 at 12:50 PM, David Vrabel <david.vrabel@citrix.com> wrote:
> On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
>> 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.
>
>>
>> Iurii Konovalenko (2):
>>   xen-sndfront: switch to the v2 driver
>>   xen-sndfront: add capture support
>>
>> Oleksandr Dmytryshyn (1):
>>   xen-sndfront: add sound frontend driver
>>
>>  include/xen/interface/io/sndif.h |  140 ++++
>
> This ABI needs to be properly defined and documented and accepted into
> Xen before we can take any frontend drivers using it.
I'll push this ABI to xen.

> David
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/3] xen-sndfront: switch to the v2 driver
  2015-01-19 10:57   ` David Vrabel
@ 2015-01-19 11:17     ` Oleksandr Dmytryshyn
  0 siblings, 0 replies; 10+ messages in thread
From: Oleksandr Dmytryshyn @ 2015-01-19 11:17 UTC (permalink / raw)
  To: David Vrabel; +Cc: Takashi Iwai, Iurii Konovalenko, Jaroslav Kysela, xen-devel

On Mon, Jan 19, 2015 at 12:57 PM, David Vrabel <david.vrabel@citrix.com> wrote:
> On 19/01/15 08:19, Oleksandr Dmytryshyn wrote:
>> From: Iurii Konovalenko <iurii.konovalenko@globallogic.com>
>>
>> Now this 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.
>>
> [...]
>>  struct sndif_request_open {
>> -     unsigned int dev_num;
>> -     unsigned int card_num;
>> -     unsigned int dev_type;
>> +     struct alsa_hwparams hwparams;
>
> Don't put ALSA specific structures in the ABI.  The protocol needs to be
> suitable for frontend drivers that aren't ALSA.
This structure is defined upper in this file.
I'll rename this structure in the next patch-set.

> David

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-01-19 11:17 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-19  8:19 [PATCH 0/3] Add sound frontend driver Oleksandr Dmytryshyn
2015-01-19  8:19 ` [PATCH 1/3] xen-sndfront: add " Oleksandr Dmytryshyn
2015-01-19 10:55   ` David Vrabel
2015-01-19 11:13     ` Oleksandr Dmytryshyn
2015-01-19  8:19 ` [PATCH 2/3] xen-sndfront: switch to the v2 driver Oleksandr Dmytryshyn
2015-01-19 10:57   ` David Vrabel
2015-01-19 11:17     ` Oleksandr Dmytryshyn
2015-01-19  8:19 ` [PATCH 3/3] xen-sndfront: add capture support Oleksandr Dmytryshyn
2015-01-19 10:50 ` [PATCH 0/3] Add sound frontend driver David Vrabel
2015-01-19 11:14   ` 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.