[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 2/3] xen-sndfront: switch to the v2 driver



From: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>

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@xxxxxxxxxxxxxxx>
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx>
---
 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


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.