* [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.