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

Re: [Xen-devel] [PATCH v3 1/2] xen-sndfront: add sound frontend driver



On Wed, Jan 21, 2015 at 5:14 PM, Stefano Stabellini
<stefano.stabellini@xxxxxxxxxxxxx> wrote:
> On Tue, 20 Jan 2015, Oleksandr Dmytryshyn wrote:
>> This is Para-virtual sound driver.
>>
>> Frontend driver registers an virtual sound card
>> and sends an PCM streams to the backend driver.
>> Backend driver is an user-space application and
>> uses ALSA with dmix plugin to play audio.
>>
>> Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx>
>> Signed-off-by: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>
>
> The code isn't bad but we still lack a document that describes how the
> protocol works.  It should have enough details in it to allow somebody
> to write a backend for it without looking at the frontend code.
>
> Without it, it is very difficult to tell if the frontend implementation
> is right.
I'll describe a protocol in the next patch-set. There will be some
description inside the file sndif.h.
The rest I'll describe additionally.

>
>>  include/xen/interface/io/sndif.h |  208 ++++++
>>  sound/drivers/Kconfig            |   10 +
>>  sound/drivers/Makefile           |    2 +
>>  sound/drivers/xen-sndfront.c     | 1478 
>> ++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 1698 insertions(+)
>>  create mode 100644 include/xen/interface/io/sndif.h
>>  create mode 100644 sound/drivers/xen-sndfront.c
>>
>> diff --git a/include/xen/interface/io/sndif.h 
>> b/include/xen/interface/io/sndif.h
>> new file mode 100644
>> index 0000000..99e6f8e
>> --- /dev/null
>> +++ b/include/xen/interface/io/sndif.h
>> @@ -0,0 +1,208 @@
>> +/******************************************************************************
>> + * sndif.h
>> + *
>> + * Unified sound-device I/O interface for Xen guest OSes.
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a 
>> copy
>> + * of this software and associated documentation files (the "Software"), to
>> + * deal in the Software without restriction, including without limitation 
>> the
>> + * rights to use, copy, modify, merge, publish, distribute, sublicense, 
>> and/or
>> + * sell copies of the Software, and to permit persons to whom the Software 
>> is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included 
>> in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
>> OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
>> THE
>> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>> + * DEALINGS IN THE SOFTWARE.
>> + *
>> + * Copyright (C) 2013-2015 GlobalLogic Inc.
>> + */
>> +
>> +#ifndef __XEN_PUBLIC_IO_SNDIF_H__
>> +#define __XEN_PUBLIC_IO_SNDIF_H__
>> +
>> +#include <xen/interface/io/ring.h>
>> +#include <xen/interface/grant_table.h>
>> +
>> +/*
>> + * Feature and Parameter Negotiation
>> + * =================================
>> + * The two halves of a Xen vsnd driver utilize nodes within the XenStore to
>> + * communicate capabilities and to negotiate operating parameters.  This
>> + * section enumerates these nodes which reside in the respective front and
>> + * backend portions of the XenStore, following the XenBus convention.
>> + *
>> + * All data in the XenStore is stored as strings.  Nodes specifying numeric
>> + * values are encoded in decimal.  Integer value ranges listed below are
>> + * expressed as fixed sized integer types capable of storing the conversion
>> + * of a properly formated node string, without loss of information.
>> + *
>> + * Any specified default value is in effect if the corresponding XenBus node
>> + * is not present in the XenStore.
>> + *
>> + * XenStore nodes in sections marked "PRIVATE" are solely for use by the
>> + * driver side whose XenBus tree contains them.
>> + *
>> + 
>> *****************************************************************************
>> + *                            Backend XenBus Nodes
>> + 
>> *****************************************************************************
>> + *
>> + *------------------ Backend Device Identification (PRIVATE) 
>> ------------------
>> + *
>> + * stream_id
>> + *      Values:         <uint32_t>
>> + *
>> + *      Virtuelized stream number
>> + *
>> + 
>> *****************************************************************************
>> + *                            Frontend XenBus Nodes
>> + 
>> *****************************************************************************
>> + *
>> + *----------------------- Request Transport Parameters 
>> -----------------------
>> + *
>> + * event-channel
>> + *      Values:         <uint32_t>
>> + *
>> + *      The identifier of the Xen event channel used to signal activity
>> + *      in the ring buffer.
>> + *
>> + * ring-ref
>> + *      Values:         <uint32_t>
>> + *      Notes:          6
>> + *
>> + *      The Xen grant reference granting permission for the backend to map
>> + *      the sole page in a single page sized ring buffer.
>> + */
>> +
>> +/*
>> + * PCM FORMATS.
>> + */
>> +#define SNDIF_PCM_FORMAT_S8          (0)
>> +#define SNDIF_PCM_FORMAT_U8          (1)
>> +#define SNDIF_PCM_FORMAT_S16_LE              (2)
>> +#define SNDIF_PCM_FORMAT_S16_BE              (3)
>> +#define SNDIF_PCM_FORMAT_U16_LE              (4)
>> +#define SNDIF_PCM_FORMAT_U16_BE              (5)
>> +
>> +/* low three bytes */
>> +#define SNDIF_PCM_FORMAT_S24_LE              (6)
>> +
>> +/* low three bytes */
>> +#define SNDIF_PCM_FORMAT_S24_BE              (7)
>> +
>> +/* low three bytes */
>> +#define SNDIF_PCM_FORMAT_U24_LE              (8)
>> +
>> +/* low three bytes */
>> +#define SNDIF_PCM_FORMAT_U24_BE              (9)
>> +
>> +#define SNDIF_PCM_FORMAT_S32_LE              (10)
>> +#define SNDIF_PCM_FORMAT_S32_BE              (11)
>> +#define SNDIF_PCM_FORMAT_U32_LE              (12)
>> +#define SNDIF_PCM_FORMAT_U32_BE              (13)
>> +
>> +/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
>> +#define SNDIF_PCM_FORMAT_FLOAT_LE    (14)
>> +
>> +/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
>> +#define SNDIF_PCM_FORMAT_FLOAT_BE    (15)
>> +
>> +/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
>> +#define SNDIF_PCM_FORMAT_FLOAT64_LE  (16)
>> +
>> +/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
>> +#define SNDIF_PCM_FORMAT_FLOAT64_BE  (17)
>> +
>> +/* IEC-958 subframe, Little Endian */
>> +#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_LE (18)
>> +
>> +/* IEC-958 subframe, Big Endian */
>> +#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_BE (19)
>> +
>> +#define SNDIF_PCM_FORMAT_MU_LAW              (20)
>> +#define SNDIF_PCM_FORMAT_A_LAW               (21)
>> +#define SNDIF_PCM_FORMAT_IMA_ADPCM   (22)
>> +#define SNDIF_PCM_FORMAT_MPEG                (23)
>> +#define SNDIF_PCM_FORMAT_GSM         (24)
>> +#define SNDIF_PCM_FORMAT_SPECIAL     (31)
>> +
>> +/*
>> + * REQUEST CODES.
>> + */
>> +#define SNDIF_OP_OPEN                        0
>> +#define SNDIF_OP_CLOSE                       1
>> +#define SNDIF_OP_READ                        2
>> +#define SNDIF_OP_WRITE                       3
>> +#define SNDIF_SET_VOLUME             4
>> +#define SNDIF_GET_VOLUME             5
>> +
>> +#define SNDIF_MAX_PAGES_PER_REQUEST  10
>> +
>> +/*
>> + * STATUS RETURN CODES.
>> + */
>> + /* Operation failed for some unspecified reason (-EIO). */
>> +#define SNDIF_RSP_ERROR       -1
>> + /* Operation completed successfully. */
>> +#define SNDIF_RSP_OKAY         0
>> +
>> +struct snd_params {
>> +     uint32_t format;        /* SNDIF_PCM_FORMAT_??? */
>> +     uint32_t channels;      /* Channels count       */
>> +     uint32_t rate;          /* Data rate            */
>> +};
>> +
>> +struct sndif_request_common {
>> +     uint64_t id;            /* private guest value, echoed in resp  */
>> +     struct snd_params _pad1;
>> +     uint32_t _pad2;
>> +     uint32_t _pad3;
>> +};
>> +
>> +struct sndif_request_open {
>> +     uint64_t id;            /* private guest value, echoed in resp  */
>> +     struct snd_params snd_params;
>> +     uint32_t stream;
>> +     uint32_t _pad2;
>> +};
>> +
>> +struct sndif_request_rw {
>> +     uint64_t id;            /* private guest value, echoed in resp  */
>> +     struct snd_params _pad1;
>> +     uint32_t len;
>> +     uint32_t _pad2;
>> +     grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST];
>> +};
>> +
>> +struct sndif_request_volume {
>> +     uint64_t id;            /* private guest value, echoed in resp  */
>> +     struct snd_params _pad1;
>> +     uint32_t left;
>> +     uint32_t right;
>> +};
>> +
>> +struct sndif_request {
>> +     uint8_t operation;      /* SNDIF_OP_??? */
>> +     union {
>> +             struct sndif_request_common common;
>> +             struct sndif_request_open open;
>> +             struct sndif_request_rw rw;
>> +             struct sndif_request_volume vol;
>> +     } u;
>> +};
>> +
>> +struct sndif_response {
>> +     uint64_t id;            /* copied from request */
>> +     uint8_t operation;      /* copied from request */
>> +     int16_t status;         /* SNDIF_RSP_???       */
>> +};
>> +
>> +DEFINE_RING_TYPES(sndif, struct sndif_request, struct sndif_response);
>> +
>> +#endif /* __XEN_PUBLIC_IO_SNDIF_H__ */
>> diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
>> index 8545da9..cd3db5a 100644
>> --- a/sound/drivers/Kconfig
>> +++ b/sound/drivers/Kconfig
>> @@ -24,6 +24,16 @@ config SND_AC97_CODEC
>>       select AC97_BUS
>>       select SND_VMASTER
>>
>> +config XEN_SND_FRONTEND
>> +     tristate "Xen virtual audio front-end driver support"
>> +     depends on SND && XEN_DOMU
>> +     default n
>> +     select SND_PCM
>> +     help
>> +       This driver implements the front-end of the Xen virtual
>> +       audio driver.  It communicates with a back-end
>> +       in another domain.
>
> I would prefer something like:
>
>       This driver provides support for Xen paravirtual sound
>           devices exported by a Xen sound driver domain (often
>           domain 0).
>
>           The corresponding backend leaves typically in userspace.
>
>           If you are compiling a kernel for use as Xen guest, you
>           should say Y here. To compile this driver as a module, chose
>           M here: the module will be called xen-sndfront.
>
> Shamelessly taken from the description of xen-netfront.
I'll use it in the next patch-set.

>
>>  menuconfig SND_DRIVERS
>>       bool "Generic sound devices"
>>       default y
>> diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
>> index 1a8440c..f9f7e19 100644
>> --- a/sound/drivers/Makefile
>> +++ b/sound/drivers/Makefile
>> @@ -11,6 +11,7 @@ snd-portman2x4-objs := portman2x4.o
>>  snd-serial-u16550-objs := serial-u16550.o
>>  snd-virmidi-objs := virmidi.o
>>  snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
>> +xen-sndfrontend-objs := xen-sndfront.o
>>
>>  # Toplevel Module Dependency
>>  obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
>> @@ -21,5 +22,6 @@ obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
>>  obj-$(CONFIG_SND_MTS64) += snd-mts64.o
>>  obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
>>  obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
>> +obj-$(CONFIG_XEN_SND_FRONTEND) += xen-sndfrontend.o
>>
>>  obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
>> diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
>> new file mode 100644
>> index 0000000..d0367c2
>> --- /dev/null
>> +++ b/sound/drivers/xen-sndfront.c
>> @@ -0,0 +1,1478 @@
>> +/*
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License version 2
>> + * as published by the Free Software Foundation; or, when distributed
>> + * separately from the Linux kernel or incorporated into other
>> + * software packages, subject to the following license:
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a 
>> copy
>> + * of this source file (the "Software"), to deal in the Software without
>> + * restriction, including without limitation the rights to use, copy, 
>> modify,
>> + * merge, publish, distribute, sublicense, and/or sell copies of the 
>> Software,
>> + * and to permit persons to whom the Software is furnished to do so, 
>> subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included 
>> in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
>> OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
>> THE
>> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
>> DEALINGS
>> + * IN THE SOFTWARE.
>> + *
>> + * Copyright (C) 2013-2015 GlobalLogic Inc.
>> + */
>> +
>> +#include <linux/init.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/slab.h>
>> +#include <linux/time.h>
>> +#include <linux/wait.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/math64.h>
>> +#include <linux/module.h>
>> +#include <linux/vmalloc.h>
>> +#include <sound/core.h>
>> +#include <sound/control.h>
>> +#include <sound/tlv.h>
>> +#include <sound/pcm.h>
>> +#include <sound/rawmidi.h>
>> +#include <sound/info.h>
>> +#include <sound/initval.h>
>> +
>> +#include <linux/uaccess.h>
>> +
>> +#include <xen/xen.h>
>> +#include <xen/events.h>
>> +#include <xen/page.h>
>> +#include <xen/grant_table.h>
>> +#include <xen/xenbus.h>
>> +#include <xen/interface/grant_table.h>
>> +
>> +#include <xen/interface/io/protocols.h>
>> +#include <xen/interface/io/sndif.h>
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}");
>> +
>> +#define VSND_WAIT_ANSWER_TOUT        5000
>> +
>> +#define MAX_PCM_DEVICES              1
>> +#define MAX_PCM_SUBSTREAMS   1
>> +#define MAX_BUFFER_SIZE              (64*1024)
>> +
>> +/* defaults */
>> +#define MIN_PERIOD_SIZE              64
>> +#define MAX_PERIOD_SIZE              (4*1024)
>> +#define USE_FORMATS          (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
>> +#define USE_RATE             (SNDRV_PCM_RATE_CONTINUOUS |\
>> +                              SNDRV_PCM_RATE_8000_48000)
>> +#define USE_RATE_MIN         5500
>> +#define USE_RATE_MAX         48000
>> +#define USE_CHANNELS_MIN     1
>> +#define USE_CHANNELS_MAX     2
>> +#define USE_PERIODS_MIN              1
>> +#define USE_PERIODS_MAX              1024
>> +
>> +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;   /* Index 0-MAX */
>> +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;    /* ID for this card */
>> +static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
>> +static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
>> +
>> +module_param_array(index, int, NULL, 0444);
>> +MODULE_PARM_DESC(index, "Index value for virtual soundcard.");
>> +module_param_array(id, charp, NULL, 0444);
>> +MODULE_PARM_DESC(id, "ID string for virtual soundcard.");
>> +module_param_array(enable, bool, NULL, 0444);
>> +MODULE_PARM_DESC(enable, "Enable this virtual soundcard.");
>> +module_param_array(model, charp, NULL, 0444);
>> +MODULE_PARM_DESC(model, "Soundcard model.");
>> +
>> +#define MIXER_ADDR_MASTER_IN 0
>> +#define MIXER_ADDR_MASTER_OUT        1
>> +#define MIXER_ADDR_LAST                      MIXER_ADDR_MASTER_OUT
>> +
>> +struct vsnd_card {
>> +     struct sndfront_info *fr_info;
>> +     unsigned int stream_id;
>> +     grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST];
>> +     unsigned char *buf;
>> +};
>> +
>> +#define SND_RING_SIZE __CONST_RING_SIZE(sndif, PAGE_SIZE)
>> +
>> +enum sndif_state {
>> +     SNDIF_STATE_DISCONNECTED,
>> +     SNDIF_STATE_CONNECTED,
>> +     SNDIF_STATE_SUSPENDED,
>> +};
>> +
>> +struct sndfront_info {
>> +     struct mutex mutex;             /* protect sndfront closing state */
>> +     struct completion completion;
>> +     spinlock_t io_lock;             /* protect 'connected' member */
>> +     struct xenbus_device *xbdev;
>> +     enum sndif_state connected;
>> +     int ring_ref;
>> +     struct sndif_front_ring ring;
>> +     unsigned int evtchn, irq;
>> +     struct vsnd_card *vcard;
>> +     int bret_code;
>> +     struct platform_device *card_dev;
>> +};
>> +
>> +#define GRANT_INVALID_REF    0
>> +
>> +struct virtualcard_model {
>> +     const char *name;
>> +     u64 formats;
>> +     size_t buffer_bytes_max;
>> +     size_t period_bytes_min;
>> +     size_t period_bytes_max;
>> +     unsigned int periods_min;
>> +     unsigned int periods_max;
>> +     unsigned int rates;
>> +     unsigned int rate_min;
>> +     unsigned int rate_max;
>> +     unsigned int channels_min;
>> +     unsigned int channels_max;
>> +};
>> +
>> +struct stream_info {
>> +     snd_pcm_uframes_t position;     /* Current position */
>> +     snd_pcm_uframes_t crossed;      /* Number of crossed writes*/
>> +     snd_pcm_format_t format;        /* SNDRV_PCM_FORMAT_* */
>> +     unsigned int rate;              /* rate in Hz */
>> +     unsigned int channels;          /* channels */
>> +     bool opened;                    /* opened status */
>> +};
>> +
>> +struct snd_virtualcard {
>> +     struct snd_card *card;
>> +     struct virtualcard_model *model;
>> +     struct snd_pcm *pcm;
>> +     struct snd_pcm_hardware pcm_hw;
>> +     spinlock_t mixer_lock;          /* protect mixer settings */
>> +     int mixer_volume[MIXER_ADDR_LAST+1][2];
>> +     int capture_source[MIXER_ADDR_LAST+1][2];
>> +     struct sndfront_info *fr_info;
>> +     struct stream_info streams[2];
>> +};
>> +
>> +/*
>> + * card models
>> + */
>> +
>> +struct virtualcard_model model_ac97 = {
>> +     .name = "ac97",
>> +     .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
>> +     .channels_min = 1,
>> +     .channels_max = 2,
>> +     .rates = SNDRV_PCM_RATE_8000_48000,
>> +     .rate_min = 8000,
>> +     .rate_max = 48000,
>> +};
>> +
>> +struct virtualcard_model *virtualcard_models[] = {
>> +     &model_ac97,
>> +     NULL
>> +};
>> +
>> +/*
>> + * PCM interface
>> + */
>> +
>> +static struct snd_pcm_hardware virtualcard_pcm_hardware = {
>> +     .info =                 (SNDRV_PCM_INFO_MMAP |
>> +                              SNDRV_PCM_INFO_INTERLEAVED |
>> +                              SNDRV_PCM_INFO_RESUME |
>> +                              SNDRV_PCM_INFO_MMAP_VALID),
>> +     .formats =              USE_FORMATS,
>> +     .rates =                USE_RATE,
>> +     .rate_min =             USE_RATE_MIN,
>> +     .rate_max =             USE_RATE_MAX,
>> +     .channels_min =         USE_CHANNELS_MIN,
>> +     .channels_max =         USE_CHANNELS_MAX,
>> +     .buffer_bytes_max =     MAX_BUFFER_SIZE,
>> +     .period_bytes_min =     MIN_PERIOD_SIZE,
>> +     .period_bytes_max =     MAX_PERIOD_SIZE,
>> +     .periods_min =          USE_PERIODS_MIN,
>> +     .periods_max =          USE_PERIODS_MAX,
>> +     .fifo_size =            0,
>> +};
>> +
>> +static inline
>> +struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard,
>> +                                  struct snd_pcm_substream *substream)
>> +{
>> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> +             return &virtualcard->streams[0];
>> +     else
>> +             return &virtualcard->streams[1];
>> +}
>> +
>> +static unsigned long vmalloc_to_mfn(void *address)
>> +{
>> +     return pfn_to_mfn(vmalloc_to_pfn(address));
>> +}
>> +
>> +static inline void flush_requests(struct sndfront_info *info)
>> +{
>> +     int notify;
>> +
>> +     RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify);
>> +
>> +     if (notify)
>> +             notify_remote_via_irq(info->irq);
>> +}
>> +
>> +static int sndif_queue_request_open(struct sndfront_info *info,
>> +                                 snd_pcm_format_t format,
>> +                                 unsigned int channels,
>> +                                 unsigned int rate)
>> +{
>> +     struct sndif_request *req;
>> +
>> +     if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
>> +             return 1;
>> +
>> +     req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
>> +
>> +     req->operation = SNDIF_OP_OPEN;
>> +     req->u.open.id = info->vcard->stream_id;
>> +     req->u.open.snd_params.format = format;
>> +     req->u.open.snd_params.channels = channels;
>> +     req->u.open.snd_params.rate = rate;
>> +     info->ring.req_prod_pvt++;
>> +
>> +     flush_requests(info);
>> +     return 0;
>> +}
>> +
>> +static int sndif_queue_request_close(struct sndfront_info *info)
>> +{
>> +     struct sndif_request *req;
>> +
>> +     if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
>> +             return 1;
>> +
>> +     req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
>> +
>> +     req->operation = SNDIF_OP_CLOSE;
>> +     req->u.open.id = info->vcard->stream_id;
>> +
>> +     info->ring.req_prod_pvt++;
>> +
>> +     flush_requests(info);
>> +     return 0;
>> +}
>> +
>> +static int sndif_queue_request_write(struct sndfront_info *info,
>> +                                  unsigned int len)
>> +{
>> +     struct sndif_request *req;
>> +     grant_ref_t *gref;
>> +     int i;
>> +
>> +     if (unlikely(info->connected != SNDIF_STATE_CONNECTED))
>> +             return 1;
>> +
>> +     req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
>> +
>> +     req->operation = SNDIF_OP_WRITE;
>> +     req->u.rw.id = info->vcard->stream_id;
>> +
>> +     req->u.rw.len = len;
>> +
>> +     gref = info->vcard->grefs;
>> +
>> +     for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
>> +             req->u.rw.gref[i] = gref[i];
>
> Do you actually need to write all the refs up to
> SNDIF_MAX_PAGES_PER_REQUEST?
> What kind of data these grefs are supposed to carry?
> What is their life cycle?
I'll simply modify drivers to use only needed refs.
An PCM-streams are transferred  between the frontend and backend
using shared pages.
grefs life cycle: sending request - receiving response in the frontend driver.

>
>> +     info->ring.req_prod_pvt++;
>> +
>> +     flush_requests(info);
>> +     return 0;
>> +}
>> +
>> +static int alsa_pcm_open(struct sndfront_info *info,
>> +                      snd_pcm_format_t format,
>> +                      unsigned int channels,
>> +                      unsigned int rate)
>> +{
>> +     unsigned long answer_tout;
>> +
>
> why calling this function "alsa_pcm_write"? It doesn't look like it has
> anything to do with alsa.  Same goes for all the other functions called
> alsa_*.
I'll rename all alsa_* functions in the next patch-set.

>
>> +     reinit_completion(&info->completion);
>> +
>> +     if (sndif_queue_request_open(info, format, channels, rate))
>> +             return -EIO;
>> +
>> +     answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
>> +     if (wait_for_completion_interruptible_timeout(&info->completion,
>> +                                                   answer_tout) <= 0)
>> +             return -ETIMEDOUT;
>> +
>> +     return info->bret_code;
>> +}
>> +
>> +static int alsa_pcm_close(struct sndfront_info *info)
>> +{
>> +     unsigned long answer_tout;
>> +
>> +     reinit_completion(&info->completion);
>> +
>> +     if (sndif_queue_request_close(info))
>> +             return -EIO;
>> +
>> +     answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
>> +     if (wait_for_completion_interruptible_timeout(&info->completion,
>> +                                                   answer_tout) <= 0)
>> +             return -ETIMEDOUT;
>> +
>> +     return info->bret_code;
>> +}
>> +
>> +static int alsa_pcm_write(struct sndfront_info *info, char __user *buf,
>> +                       int len)
>> +{
>> +     unsigned char *shared_data;
>> +     unsigned long answer_tout;
>> +
>> +     shared_data = info->vcard->buf;
>> +
>> +     if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
>> +             return -EFAULT;
>> +
>> +     if (copy_from_user(shared_data, buf, len))
>> +             return -EFAULT;
>> +
>> +     reinit_completion(&info->completion);
>> +
>> +     if (sndif_queue_request_write(info, len))
>> +             return -EIO;
>> +
>> +     answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
>> +     if (wait_for_completion_interruptible_timeout(&info->completion,
>> +                                                   answer_tout) <= 0)
>> +             return -ETIMEDOUT;
>> +
>> +     return info->bret_code;
>> +}
>> +
>> +static int alsa_pcm_silence(struct sndfront_info *info, int len)
>> +{
>> +     unsigned char *shared_data;
>> +     unsigned long answer_tout;
>> +
>> +     shared_data = info->vcard->buf;
>> +
>> +     if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
>> +             return -EFAULT;
>> +
>> +     memset(shared_data, 0, len);
>> +
>> +     reinit_completion(&info->completion);
>> +
>> +     if (sndif_queue_request_write(info, len))
>> +             return -EIO;
>> +
>> +     answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
>> +     if (wait_for_completion_interruptible_timeout(&info->completion,
>> +                                                   answer_tout) <= 0)
>> +             return -ETIMEDOUT;
>> +
>> +     return info->bret_code;
>> +}
>> +
>> +static int sndif_setup_vcard(struct sndfront_info *info)
>> +{
>> +     grant_ref_t gref_head;
>> +     unsigned long mfn;
>> +     int ref;
>> +     int i;
>> +     int ret;
>> +
>> +     info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE);
>> +     if (!info->vcard->buf) {
>> +             ret = -ENOMEM;
>> +             goto err_ret;
>> +     }
>> +
>> +     ret = gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST,
>> +                                         &gref_head);
>> +     if (ret)
>> +             goto err_vstream_free_buf;
>> +
>> +     for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) {
>> +             ref = gnttab_claim_grant_reference(&gref_head);
>> +             BUG_ON(ref == -ENOSPC);
>> +
>> +             mfn = vmalloc_to_mfn(info->vcard->buf + PAGE_SIZE * i);
>> +
>> +             gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
>> +                                             mfn, 0);
>> +
>> +             info->vcard->grefs[i] = ref;
>> +     }
>> +
>> +     gnttab_free_grant_references(gref_head);
>> +     return ret;
>> +
>> +err_vstream_free_buf:
>> +     vfree(info->vcard->buf);
>> +     for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
>> +             gnttab_end_foreign_access(info->vcard->grefs[i], 0, 0UL);
>> +
>> +     gnttab_free_grant_references(gref_head);
>> +err_ret:
>> +     return ret;
>> +}
>> +
>> +static void sndif_cleanup_vcard(struct sndfront_info *info)
>> +{
>> +     int i;
>> +
>> +     if (info->vcard->buf) {
>> +             vfree(info->vcard->buf);
>> +             for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++)
>> +                     gnttab_end_foreign_access(info->vcard->grefs[i],
>> +                                               0, 0UL);
>> +     }
>> +}
>> +
>> +static int sndif_add_virt_devices(struct sndfront_info *info,
>> +                               unsigned int stream_id)
>> +{
>> +     int ret = 0;
>> +
>> +     struct vsnd_card *vcard;
>> +
>> +     vcard = kmalloc(sizeof(*vcard), GFP_KERNEL);
>> +
>> +     if (!vcard)
>> +             return -ENOMEM;
>> +
>> +     vcard->stream_id = stream_id;
>> +     vcard->fr_info = info;
>> +
>> +     info->vcard = vcard;
>> +
>> +     sndif_setup_vcard(info);
>> +
>> +     return ret;
>> +}
>> +
>> +static void sndif_cleanup_virt_devices(struct sndfront_info *info)
>> +{
>> +     if (info->vcard) {
>> +             sndif_cleanup_vcard(info);
>> +             kfree(info->vcard);
>> +     }
>> +}
>> +
>> +static void sndif_free(struct sndfront_info *info, int suspend)
>> +{
>> +     /* Free resources associated with old device channel. */
>> +     if (info->ring_ref != GRANT_INVALID_REF) {
>> +             gnttab_end_foreign_access(info->ring_ref, 0,
>> +                                       (unsigned long)info->ring.sring);
>> +             info->ring_ref = GRANT_INVALID_REF;
>> +             info->ring.sring = NULL;
>> +     }
>> +     if (info->irq)
>> +             unbind_from_irqhandler(info->irq, info);
>> +     info->evtchn = 0;
>> +     info->irq = 0;
>> +}
>> +
>> +static irqreturn_t sndif_interrupt(int irq, void *data)
>> +{
>> +     struct sndif_response *bret;
>> +     RING_IDX i, rp;
>> +     unsigned long flags;
>> +     struct sndfront_info *info = (struct sndfront_info *)data;
>> +     int error;
>> +
>> +     spin_lock_irqsave(&info->io_lock, flags);
>> +
>> +     if (unlikely(info->connected != SNDIF_STATE_CONNECTED)) {
>> +             spin_unlock_irqrestore(&info->io_lock, flags);
>> +             return IRQ_HANDLED;
>> +     }
>> +
>> + again:
>> +     rp = info->ring.sring->rsp_prod;
>> +     rmb(); /* Ensure we see queued responses up to 'rp'. */
>> +
>> +     for (i = info->ring.rsp_cons; i != rp; i++) {
>> +             unsigned long id;
>> +
>> +             bret = RING_GET_RESPONSE(&info->ring, i);
>> +             id   = bret->id;
>> +
>> +             error = (bret->status == SNDIF_RSP_OKAY) ? 0 : -EIO;
>> +             switch (bret->operation) {
>> +             case SNDIF_OP_OPEN:
>> +             case SNDIF_OP_CLOSE:
>> +             case SNDIF_OP_WRITE:
>> +                     if (unlikely(bret->status != SNDIF_RSP_OKAY))
>> +                             dev_dbg(&info->xbdev->dev,
>> +                                     "snddev data request error: %x\n",
>> +                                     bret->status);
>> +
>> +                     info->bret_code = bret->status;
>> +                     complete(&info->completion);
>> +                     break;
>> +
>> +             default:
>> +                     BUG();
>> +             }
>> +     }
>> +
>> +     info->ring.rsp_cons = i;
>> +
>> +     if (i != info->ring.req_prod_pvt) {
>> +             int more_to_do;
>> +
>> +             RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
>> +             if (more_to_do)
>> +                     goto again;
>> +     } else {
>> +             info->ring.sring->rsp_event = i + 1;
>> +     }
>> +
>> +     spin_unlock_irqrestore(&info->io_lock, flags);
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int setup_sndring(struct xenbus_device *dev,
>> +                      struct sndfront_info *info)
>> +{
>> +     struct sndif_sring *sring;
>> +     int err;
>> +
>> +     info->ring_ref = GRANT_INVALID_REF;
>> +
>> +     sring = (struct sndif_sring *)__get_free_page(GFP_NOIO | __GFP_HIGH);
>> +     if (!sring) {
>> +             xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
>> +             return -ENOMEM;
>> +     }
>> +     SHARED_RING_INIT(sring);
>> +     FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
>> +
>> +     err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
>> +     if (err < 0) {
>> +             free_page((unsigned long)sring);
>> +             info->ring.sring = NULL;
>> +             goto fail;
>> +     }
>> +     info->ring_ref = err;
>> +
>> +     err = xenbus_alloc_evtchn(dev, &info->evtchn);
>> +     if (err)
>> +             goto fail;
>> +
>> +     err = bind_evtchn_to_irqhandler(info->evtchn, sndif_interrupt, 0,
>> +                                     "sndif", info);
>> +     if (err <= 0) {
>> +             xenbus_dev_fatal(dev, err,
>> +                              "bind_evtchn_to_irqhandler failed");
>> +             goto fail;
>> +     }
>> +     info->irq = err;
>> +
>> +     return 0;
>> +fail:
>> +     sndif_free(info, 0);
>> +     return err;
>> +}
>> +
>> +/* Common code used when first setting up, and when resuming. */
>> +static int talk_to_sndback(struct xenbus_device *dev,
>> +                        struct sndfront_info *info)
>> +{
>> +     const char *message = NULL;
>> +     struct xenbus_transaction xbt;
>> +     int err;
>> +
>> +     /* Create shared ring, alloc event channel. */
>> +     err = setup_sndring(dev, info);
>> +     if (err)
>> +             goto out;
>> +
>> +again:
>> +     err = xenbus_transaction_start(&xbt);
>> +     if (err) {
>> +             xenbus_dev_fatal(dev, err, "starting transaction");
>> +             goto destroy_sndring;
>> +     }
>> +
>> +     err = xenbus_printf(xbt, dev->nodename,
>> +                         "ring-ref", "%u", info->ring_ref);
>> +     if (err) {
>> +             message = "writing ring-ref";
>> +             goto abort_transaction;
>> +     }
>> +     err = xenbus_printf(xbt, dev->nodename,
>> +                         "event-channel", "%u", info->evtchn);
>> +     if (err) {
>> +             message = "writing event-channel";
>> +             goto abort_transaction;
>> +     }
>> +
>> +     err = xenbus_transaction_end(xbt, 0);
>> +     if (err) {
>> +             if (err == -EAGAIN)
>> +                     goto again;
>> +             xenbus_dev_fatal(dev, err, "completing transaction");
>> +             goto destroy_sndring;
>> +     }
>> +
>> +     xenbus_switch_state(dev, XenbusStateInitialised);
>> +
>> +     return 0;
>> +
>> + abort_transaction:
>> +     xenbus_transaction_end(xbt, 1);
>> +     if (message)
>> +             xenbus_dev_fatal(dev, err, "%s", message);
>> + destroy_sndring:
>> +     sndif_free(info, 0);
>> + out:
>> +     return err;
>> +}
>> +
>> +static int virtualcard_pcm_trigger(struct snd_pcm_substream *substream, int 
>> cmd)
>> +{
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_START:
>> +     case SNDRV_PCM_TRIGGER_RESUME:
>> +             runtime->stop_threshold = runtime->buffer_size + 1;
>> +             break;
>> +     case SNDRV_PCM_TRIGGER_STOP:
>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>> +             runtime->stop_threshold = runtime->buffer_size;
>> +             break;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream)
>> +{
>> +     int err;
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct stream_info *vcard_stream;
>> +
>> +     vcard_stream = get_vcard_stream(virtualcard, substream);
>> +
>> +     if ((runtime->rate != vcard_stream->rate) ||
>> +         (runtime->channels != vcard_stream->channels) ||
>> +         (runtime->format != vcard_stream->format)) {
>> +             if (vcard_stream->opened) {
>> +                     err = alsa_pcm_close(virtualcard->fr_info);
>> +                     if (err)
>> +                             return err;
>> +
>> +                     /* if closed successfully */
>> +                     vcard_stream->opened = false;
>> +             }
>> +             err = alsa_pcm_open(virtualcard->fr_info, runtime->format,
>> +                                 runtime->channels, runtime->rate);
>> +             if (err)
>> +                     return err;
>> +
>> +             /* if opened successfully */
>> +             vcard_stream->rate = runtime->rate;
>> +             vcard_stream->channels = runtime->channels;
>> +             vcard_stream->format = runtime->format;
>> +             vcard_stream->opened = true;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static
>> +snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream 
>> *substream)
>> +{
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     snd_pcm_uframes_t buff_size = substream->runtime->buffer_size;
>> +     struct stream_info *vcard_stream;
>> +
>> +     vcard_stream = get_vcard_stream(virtualcard, substream);
>> +
>> +     if (vcard_stream->crossed) {
>> +             snd_pcm_uframes_t hw_base = substream->runtime->hw_ptr_base;
>> +
>> +             hw_base += buff_size;
>> +             if (hw_base >= substream->runtime->boundary)
>> +                     hw_base = 0;
>> +
>> +             substream->runtime->hw_ptr_base = hw_base;
>> +             vcard_stream->crossed = 0;
>> +     }
>> +     return vcard_stream->position;
>> +}
>> +
>> +static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream,
>> +                                  struct snd_pcm_hw_params *hw_params)
>> +{
>> +     return 0;
>> +}
>> +
>> +static int virtualcard_pcm_hw_free(struct snd_pcm_substream *substream)
>> +{
>> +     return 0;
>> +}
>> +
>> +static int virtualcard_pcm_open(struct snd_pcm_substream *substream)
>> +{
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct stream_info *vcard_stream;
>> +
>> +     vcard_stream = get_vcard_stream(virtualcard, substream);
>> +
>> +     vcard_stream->channels = 0;
>> +     vcard_stream->rate = 0;
>> +     vcard_stream->position = 0;
>> +     vcard_stream->crossed = 0;
>> +     vcard_stream->format = 0;
>> +     vcard_stream->opened = false;
>> +
>> +     runtime->hw = virtualcard->pcm_hw;
>> +     runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
>> +                                   SNDRV_PCM_INFO_MMAP_VALID |
>> +                                   SNDRV_PCM_INFO_DOUBLE |
>> +                                   SNDRV_PCM_INFO_BATCH |
>> +                                   SNDRV_PCM_INFO_NONINTERLEAVED |
>> +                                   SNDRV_PCM_INFO_RESUME |
>> +                                   SNDRV_PCM_INFO_PAUSE);
>> +     runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
>> +     return 0;
>> +}
>> +
>> +static int virtualcard_pcm_close(struct snd_pcm_substream *substream)
>> +{
>> +     int err;
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +
>> +     err = alsa_pcm_close(virtualcard->fr_info);
>> +     if (err)
>> +             return err;
>> +
>> +     get_vcard_stream(virtualcard, substream)->opened = false;
>> +
>> +     return 0;
>> +}
>> +
>> +static int virtualcard_pcm_playback_copy(struct snd_pcm_substream 
>> *substream,
>> +                                      int channel, snd_pcm_uframes_t pos,
>> +                                      void __user *src,
>> +                                      snd_pcm_uframes_t count)
>> +{
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct stream_info *vcard_stream = &virtualcard->streams[0];
>> +     snd_pcm_uframes_t stream_pos = vcard_stream->position;
>> +
>> +     vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
>> +     vcard_stream->crossed = count / runtime->buffer_size;
>> +     return alsa_pcm_write(virtualcard->fr_info, src,
>> +                           frames_to_bytes(runtime, count));
>> +}
>> +
>> +static int virtualcard_pcm_playback_silence(struct snd_pcm_substream 
>> *substream,
>> +                                         int channel, snd_pcm_uframes_t pos,
>> +                                         snd_pcm_uframes_t count)
>> +{
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct stream_info *vcard_stream = &virtualcard->streams[0];
>> +     snd_pcm_uframes_t stream_pos = vcard_stream->position;
>> +
>> +     vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
>> +     vcard_stream->crossed = count / runtime->buffer_size;
>> +
>> +     return alsa_pcm_silence(virtualcard->fr_info,
>> +                             frames_to_bytes(runtime, count));
>> +}
>> +
>> +static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream,
>> +                                     int channel, snd_pcm_uframes_t pos,
>> +                                     void __user *dst,
>> +                                     snd_pcm_uframes_t count)
>> +{
>> +     struct snd_virtualcard *virtualcard = 
>> snd_pcm_substream_chip(substream);
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct stream_info *vcard_stream = &virtualcard->streams[1];
>> +     snd_pcm_uframes_t stream_pos = vcard_stream->position;
>> +
>> +     vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
>> +     vcard_stream->crossed = count / runtime->buffer_size;
>> +
>> +     return 0;
>> +}
>> +
>> +static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
>> +     .open =         virtualcard_pcm_open,
>> +     .close =        virtualcard_pcm_close,
>> +     .ioctl =        snd_pcm_lib_ioctl,
>> +     .hw_params =    virtualcard_pcm_hw_params,
>> +     .hw_free =      virtualcard_pcm_hw_free,
>> +     .prepare =      virtualcard_pcm_prepare,
>> +     .trigger =      virtualcard_pcm_trigger,
>> +     .pointer =      virtualcard_pcm_pointer,
>> +     .copy =         virtualcard_pcm_playback_copy,
>> +     .silence =      virtualcard_pcm_playback_silence,
>> +};
>> +
>> +static struct snd_pcm_ops virtualcard_pcm_capture_ops = {
>> +     .open =         virtualcard_pcm_open,
>> +     .close =        virtualcard_pcm_close,
>> +     .ioctl =        snd_pcm_lib_ioctl,
>> +     .hw_params =    virtualcard_pcm_hw_params,
>> +     .hw_free =      virtualcard_pcm_hw_free,
>> +     .prepare =      virtualcard_pcm_prepare,
>> +     .trigger =      virtualcard_pcm_trigger,
>> +     .pointer =      virtualcard_pcm_pointer,
>> +     .copy =         virtualcard_pcm_capture_copy,
>> +};
>> +
>> +static int snd_card_virtualcard_pcm(struct snd_virtualcard *virtualcard,
>> +                                 int device, int substreams)
>> +{
>> +     struct snd_pcm *pcm;
>> +     int err;
>> +
>> +     err = snd_pcm_new(virtualcard->card, "Virtual card PCM", device,
>> +                       substreams, substreams, &pcm);
>> +     if (err < 0)
>> +             return err;
>> +     virtualcard->pcm = pcm;
>> +     snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
>> +                     &virtualcard_pcm_playback_ops);
>> +     snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
>> +                     &virtualcard_pcm_capture_ops);
>> +     pcm->private_data = virtualcard;
>> +     pcm->info_flags = 0;
>> +     strcpy(pcm->name, "Virtual card PCM");
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * mixer interface
>> + */
>> +
>> +#define VIRTUALCARD_VOLUME(xname, xindex, addr) {    \
>> +     .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
>> +     .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |     \
>> +               SNDRV_CTL_ELEM_ACCESS_TLV_READ,       \
>> +     .name = xname,                                  \
>> +     .index = xindex,                                \
>> +     .info = snd_virtualcard_volume_info,            \
>> +     .get = snd_virtualcard_volume_get,              \
>> +     .put = snd_virtualcard_volume_put,              \
>> +     .private_value = addr,                          \
>> +     .tlv = { .p = db_scale_virtualcard }            \
>> +}
>> +
>> +static int snd_virtualcard_volume_info(struct snd_kcontrol *kcontrol,
>> +                                    struct snd_ctl_elem_info *uinfo)
>> +{
>> +     uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
>> +     uinfo->count = 2;
>> +     uinfo->value.integer.min = -50;
>> +     uinfo->value.integer.max = 100;
>> +     return 0;
>> +}
>> +
>> +static int snd_virtualcard_volume_get(struct snd_kcontrol *kcontrol,
>> +                                   struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
>> +     int addr = kcontrol->private_value;
>> +
>> +     spin_lock_irq(&virtualcard->mixer_lock);
>> +     ucontrol->value.integer.value[0] = virtualcard->mixer_volume[addr][0];
>> +     ucontrol->value.integer.value[1] = virtualcard->mixer_volume[addr][1];
>> +     spin_unlock_irq(&virtualcard->mixer_lock);
>> +     return 0;
>> +}
>> +
>> +static int snd_virtualcard_volume_put(struct snd_kcontrol *kcontrol,
>> +                                   struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
>> +     int change, addr = kcontrol->private_value;
>> +     int left, right;
>> +
>> +     left = ucontrol->value.integer.value[0];
>> +     if (left < -50)
>> +             left = -50;
>> +     if (left > 100)
>> +             left = 100;
>> +     right = ucontrol->value.integer.value[1];
>> +     if (right < -50)
>> +             right = -50;
>> +     if (right > 100)
>> +             right = 100;
>> +     spin_lock_irq(&virtualcard->mixer_lock);
>> +     change = virtualcard->mixer_volume[addr][0] != left ||
>> +              virtualcard->mixer_volume[addr][1] != right;
>> +     virtualcard->mixer_volume[addr][0] = left;
>> +     virtualcard->mixer_volume[addr][1] = right;
>> +     spin_unlock_irq(&virtualcard->mixer_lock);
>> +     return change;
>> +}
>> +
>> +static const DECLARE_TLV_DB_SCALE(db_scale_virtualcard, -4500, 30, 0);
>> +
>> +#define VIRTUALCARD_CAPSRC(xname, xindex, addr) {    \
>> +     .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
>> +     .name = xname,                                  \
>> +     .index = xindex,                                \
>> +     .info = snd_virtualcard_capsrc_info,            \
>> +     .get = snd_virtualcard_capsrc_get,              \
>> +     .put = snd_virtualcard_capsrc_put,              \
>> +     .private_value = addr                           \
>> +}
>> +
>> +#define snd_virtualcard_capsrc_info  snd_ctl_boolean_stereo_info
>> +
>> +static int snd_virtualcard_capsrc_get(struct snd_kcontrol *kcontrol,
>> +                                   struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
>> +     int addr = kcontrol->private_value;
>> +
>> +     spin_lock_irq(&virtualcard->mixer_lock);
>> +     ucontrol->value.integer.value[0] = 
>> virtualcard->capture_source[addr][0];
>> +     ucontrol->value.integer.value[1] = 
>> virtualcard->capture_source[addr][1];
>> +     spin_unlock_irq(&virtualcard->mixer_lock);
>> +     return 0;
>> +}
>> +
>> +static int snd_virtualcard_capsrc_put(struct snd_kcontrol *kcontrol,
>> +                                   struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol);
>> +     int change, addr = kcontrol->private_value;
>> +     int left, right;
>> +
>> +     left = ucontrol->value.integer.value[0] & 1;
>> +     right = ucontrol->value.integer.value[1] & 1;
>> +     spin_lock_irq(&virtualcard->mixer_lock);
>> +     change = virtualcard->capture_source[addr][0] != left &&
>> +              virtualcard->capture_source[addr][1] != right;
>> +     virtualcard->capture_source[addr][0] = left;
>> +     virtualcard->capture_source[addr][1] = right;
>> +     spin_unlock_irq(&virtualcard->mixer_lock);
>> +     return change;
>> +}
>> +
>> +static struct snd_kcontrol_new snd_virtualcard_controls[] = {
>> +VIRTUALCARD_VOLUME("Master Out Volume", 0, MIXER_ADDR_MASTER_OUT),
>> +VIRTUALCARD_CAPSRC("Master Out Switch", 0, MIXER_ADDR_MASTER_OUT),
>> +VIRTUALCARD_VOLUME("Master In Volume", 0, MIXER_ADDR_MASTER_IN),
>> +VIRTUALCARD_CAPSRC("Master In Switch", 0, MIXER_ADDR_MASTER_IN),
>> +};
>> +
>> +static int snd_card_virtualcard_new_mixer(struct snd_virtualcard 
>> *virtualcard)
>> +{
>> +     struct snd_card *card = virtualcard->card;
>> +     struct snd_kcontrol *kcontrol;
>> +     unsigned int idx;
>> +     int err;
>> +
>> +     spin_lock_init(&virtualcard->mixer_lock);
>> +     strcpy(card->mixername, "Virtual card Mixer");
>> +
>> +     for (idx = 0; idx < ARRAY_SIZE(snd_virtualcard_controls); idx++) {
>> +             kcontrol = snd_ctl_new1(&snd_virtualcard_controls[idx],
>> +                                     virtualcard);
>> +             err = snd_ctl_add(card, kcontrol);
>> +             if (err < 0)
>> +                     return err;
>> +     }
>> +     return 0;
>> +}
>> +
>> +/* #define CONFIG_SND_DEBUG */
>> +/* #define CONFIG_PROC_FS */
>> +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
>> +/*
>> + * proc interface
>> + */
>> +static void print_formats(struct snd_virtualcard *virtualcard,
>> +                       struct snd_info_buffer *buffer)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
>> +             if (virtualcard->pcm_hw.formats & (1ULL << i))
>> +                     snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
>> +     }
>> +}
>> +
>> +static void print_rates(struct snd_virtualcard *virtualcard,
>> +                     struct snd_info_buffer *buffer)
>> +{
>> +     static int rates[] = {
>> +             8000, 11025, 16000, 22050, 32000, 44100, 48000,
>> +             64000, 88200, 96000, 176400, 192000,
>> +     };
>> +     int i;
>> +
>> +     if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
>> +             snd_iprintf(buffer, " continuous");
>> +     if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
>> +             snd_iprintf(buffer, " knot");
>> +     for (i = 0; i < ARRAY_SIZE(rates); i++)
>> +             if (virtualcard->pcm_hw.rates & (1 << i))
>> +                     snd_iprintf(buffer, " %d", rates[i]);
>> +}
>> +
>> +#define get_virtualcard_int_ptr(virtualcard, ofs) \
>> +     (unsigned int *)((char *)&((virtualcard)->pcm_hw) + (ofs))
>> +#define get_virtualcard_ll_ptr(virtualcard, ofs) \
>> +     (unsigned long long *)((char *)&((virtualcard)->pcm_hw) + (ofs))
>> +
>> +struct virtualcard_hw_field {
>> +     const char *name;
>> +     const char *format;
>> +     unsigned int offset;
>> +     unsigned int size;
>> +};
>> +
>> +#define FIELD_ENTRY(item, fmt) {                             \
>> +     .name = #item,                                          \
>> +     .format = fmt,                                          \
>> +     .offset = offsetof(struct snd_pcm_hardware, item),      \
>> +     .size = sizeof(virtualcard_pcm_hardware.item)           \
>> +}
>> +
>> +static struct virtualcard_hw_field fields[] = {
>> +     FIELD_ENTRY(formats, "%#llx"),
>> +     FIELD_ENTRY(rates, "%#x"),
>> +     FIELD_ENTRY(rate_min, "%d"),
>> +     FIELD_ENTRY(rate_max, "%d"),
>> +     FIELD_ENTRY(channels_min, "%d"),
>> +     FIELD_ENTRY(channels_max, "%d"),
>> +     FIELD_ENTRY(buffer_bytes_max, "%ld"),
>> +     FIELD_ENTRY(period_bytes_min, "%ld"),
>> +     FIELD_ENTRY(period_bytes_max, "%ld"),
>> +     FIELD_ENTRY(periods_min, "%d"),
>> +     FIELD_ENTRY(periods_max, "%d"),
>> +};
>> +
>> +static void virtualcard_proc_read(struct snd_info_entry *entry,
>> +                               struct snd_info_buffer *buffer)
>> +{
>> +     struct snd_virtualcard *virtualcard = entry->private_data;
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(fields); i++) {
>> +             snd_iprintf(buffer, "%s ", fields[i].name);
>> +             if (fields[i].size == sizeof(int))
>> +                     snd_iprintf(buffer, fields[i].format,
>> +                                 *get_virtualcard_int_ptr(virtualcard,
>> +                                                          
>> fields[i].offset));
>> +             else
>> +                     snd_iprintf(buffer, fields[i].format,
>> +                                 *get_virtualcard_ll_ptr(virtualcard,
>> +                                                         fields[i].offset));
>> +             if (!strcmp(fields[i].name, "formats"))
>> +                     print_formats(virtualcard, buffer);
>> +             else if (!strcmp(fields[i].name, "rates"))
>> +                     print_rates(virtualcard, buffer);
>> +             snd_iprintf(buffer, "\n");
>> +     }
>> +}
>> +
>> +static void virtualcard_proc_write(struct snd_info_entry *entry,
>> +                                struct snd_info_buffer *buffer)
>> +{
>> +     struct snd_virtualcard *virtualcard = entry->private_data;
>> +     char line[64];
>> +
>> +     while (!snd_info_get_line(buffer, line, sizeof(line))) {
>> +             char item[20];
>> +             const char *ptr;
>> +             unsigned long long val;
>> +             unsigned int offset;
>> +             int i;
>> +
>> +             ptr = snd_info_get_str(item, line, sizeof(item));
>> +             for (i = 0; i < ARRAY_SIZE(fields); i++) {
>> +                     if (!strcmp(item, fields[i].name))
>> +                             break;
>> +             }
>> +             if (i >= ARRAY_SIZE(fields))
>> +                     continue;
>> +             snd_info_get_str(item, ptr, sizeof(item));
>> +             if (kstrtoull(item, 0, &val))
>> +                     continue;
>> +
>> +             offset = fields[i].offset;
>> +             if (fields[i].size == sizeof(int))
>> +                     *get_virtualcard_int_ptr(virtualcard, offset) = val;
>> +             else
>> +                     *get_virtualcard_ll_ptr(virtualcard, offset) = val;
>> +     }
>> +}
>> +
>> +static void virtualcard_proc_init(struct snd_virtualcard *chip)
>> +{
>> +     struct snd_info_entry *entry;
>> +
>> +     if (!snd_card_proc_new(chip->card, "virtualcard_pcm", &entry)) {
>> +             snd_info_set_text_ops(entry, chip, virtualcard_proc_read);
>> +             entry->c.text.write = virtualcard_proc_write;
>> +             entry->mode |= S_IWUSR;
>> +             entry->private_data = chip;
>> +     }
>> +}
>> +#else
>> +#define virtualcard_proc_init(x)
>> +#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
>> +
>> +static int snd_virtualcard_probe(struct platform_device *devptr)
>> +{
>> +     struct snd_card *card;
>> +     struct snd_virtualcard *virtualcard;
>> +     struct virtualcard_model *m = NULL;
>> +     int err;
>> +     int dev = devptr->id;
>> +     struct snd_pcm_hardware *pcm_hw;
>> +
>> +     err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
>> +                        sizeof(struct snd_virtualcard), &card);
>> +     if (err < 0)
>> +             return err;
>> +     virtualcard = card->private_data;
>> +     virtualcard->card = card;
>> +     virtualcard->fr_info =
>> +             (*((struct sndfront_info **)devptr->dev.platform_data));
>> +
>> +     m = virtualcard_models[0];
>> +
>> +     err = snd_card_virtualcard_pcm(virtualcard, 0, 1);
>> +     if (err < 0)
>> +             goto __nodev;
>> +
>> +     virtualcard->pcm_hw = virtualcard_pcm_hardware;
>> +     pcm_hw = &virtualcard->pcm_hw;
>> +     if (m) {
>> +             if (m->formats)
>> +                     pcm_hw->formats = m->formats;
>> +             if (m->buffer_bytes_max)
>> +                     pcm_hw->buffer_bytes_max = m->buffer_bytes_max;
>> +             if (m->period_bytes_min)
>> +                     pcm_hw->period_bytes_min = m->period_bytes_min;
>> +             if (m->period_bytes_max)
>> +                     pcm_hw->period_bytes_max = m->period_bytes_max;
>> +             if (m->periods_min)
>> +                     pcm_hw->periods_min = m->periods_min;
>> +             if (m->periods_max)
>> +                     pcm_hw->periods_max = m->periods_max;
>> +             if (m->rates)
>> +                     pcm_hw->rates = m->rates;
>> +             if (m->rate_min)
>> +                     pcm_hw->rate_min = m->rate_min;
>> +             if (m->rate_max)
>> +                     pcm_hw->rate_max = m->rate_max;
>> +             if (m->channels_min)
>> +                     pcm_hw->channels_min = m->channels_min;
>> +             if (m->channels_max)
>> +                     pcm_hw->channels_max = m->channels_max;
>> +     }
>> +
>> +     err = snd_card_virtualcard_new_mixer(virtualcard);
>> +     if (err < 0)
>> +             goto __nodev;
>> +     strcpy(card->driver, "Virtual card");
>> +     strcpy(card->shortname, "Virtual card");
>> +     sprintf(card->longname, "Virtual card %i", dev + 1);
>> +
>> +     virtualcard_proc_init(virtualcard);
>> +
>> +     snd_card_set_dev(card, &devptr->dev);
>> +
>> +     err = snd_card_register(card);
>> +     if (err == 0) {
>> +             platform_set_drvdata(devptr, card);
>> +             return 0;
>> +     }
>> +__nodev:
>> +     snd_card_free(card);
>> +     return err;
>> +}
>> +
>> +static int snd_virtualcard_remove(struct platform_device *devptr)
>> +{
>> +     snd_card_free(platform_get_drvdata(devptr));
>> +     platform_set_drvdata(devptr, NULL);
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int snd_virtualcard_suspend(struct device *pdev)
>> +{
>> +     struct snd_card *card = dev_get_drvdata(pdev);
>> +     struct snd_virtualcard *virtualcard = card->private_data;
>> +
>> +     snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
>> +     snd_pcm_suspend_all(virtualcard->pcm);
>> +     return 0;
>> +}
>> +
>> +static int snd_virtualcard_resume(struct device *pdev)
>> +{
>> +     struct snd_card *card = dev_get_drvdata(pdev);
>> +
>> +     snd_power_change_state(card, SNDRV_CTL_POWER_D0);
>> +     return 0;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(snd_virtualcard_pm, snd_virtualcard_suspend,
>> +                      snd_virtualcard_resume);
>> +#define SND_VIRTUALCARD_PM_OPS       (&snd_virtualcard_pm)
>> +#else
>> +#define SND_VIRTUALCARD_PM_OPS       NULL
>> +#endif
>> +
>> +#define SND_VIRTUALCARD_DRIVER       "snd_virtualcard"
>> +
>> +static struct platform_driver snd_virtualcard_driver = {
>> +     .probe          = snd_virtualcard_probe,
>> +     .remove         = snd_virtualcard_remove,
>> +     .driver         = {
>> +             .name   = SND_VIRTUALCARD_DRIVER,
>> +             .owner  = THIS_MODULE,
>> +             .pm     = SND_VIRTUALCARD_PM_OPS,
>> +     },
>> +};
>> +
>> +/**
>> + * Entry point to this code when a new device is created.  Allocate the 
>> basic
>> + * structures and the ring buffer for communication with the backend, and
>> + * inform the backend of the appropriate details for those.  Switch to
>> + * Initialised state.
>> + */
>> +static int sndfront_probe(struct xenbus_device *dev,
>> +                       const struct xenbus_device_id *id)
>> +{
>> +     int err;
>> +     struct sndfront_info *info;
>> +
>> +     info = kzalloc(sizeof(*info), GFP_KERNEL);
>> +     if (!info) {
>> +             xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     mutex_init(&info->mutex);
>
> Do you really need this mutex? It doesn't look like it you are actually
> using it.
I'll remove this mutex in the next patch-set.

>
>> +     spin_lock_init(&info->io_lock);
>> +     init_completion(&info->completion);
>> +     info->xbdev = dev;
>> +
>> +     dev_set_drvdata(&dev->dev, info);
>> +
>> +     err = talk_to_sndback(dev, info);
>> +     if (err)
>> +             goto err_free_info;
>> +
>> +     info->card_dev = platform_device_register_data(NULL,
>> +                                                    SND_VIRTUALCARD_DRIVER,
>> +                                                    -1, &info, 
>> sizeof(info));
>> +     if (IS_ERR(info->card_dev)) {
>> +             err = -ENODEV;
>> +             goto err_free_info;
>> +     }
>> +     return 0;
>> +
>> +err_free_info:
>> +     kfree(info);
>> +     dev_set_drvdata(&dev->dev, NULL);
>> +     return err;
>> +}
>> +
>> +/**
>> + * We are reconnecting to the backend, due to a suspend/resume, or a backend
>> + * driver restart.  We tear down our blkif structure and recreate it, but
>> + * leave the device-layer structures intact so that this is transparent to 
>> the
>> + * rest of the kernel.
>> + */
>> +static int sndfront_resume(struct xenbus_device *dev)
>> +{
>> +     struct sndfront_info *info = dev_get_drvdata(&dev->dev);
>> +     int err;
>> +
>> +     dev_dbg(&dev->dev, "sndfront_resume: %s\n", dev->nodename);
>> +
>> +     sndif_free(info, info->connected == SNDIF_STATE_CONNECTED);
>> +
>> +     err = talk_to_sndback(dev, info);
>> +     if (info->connected == SNDIF_STATE_SUSPENDED && !err)
>> +             info->connected = SNDIF_STATE_CONNECTED;
>> +
>> +     return err;
>> +}
>> +
>> +static void
>> +sndfront_closing(struct sndfront_info *info)
>> +{
>> +     struct xenbus_device *xbdev = info->xbdev;
>> +
>> +     mutex_lock(&info->mutex);
>> +
>> +     if (xbdev->state == XenbusStateClosing) {
>> +             mutex_unlock(&info->mutex);
>> +             return;
>> +     }
>> +
>> +     mutex_unlock(&info->mutex);
>> +
>> +     xenbus_frontend_closed(xbdev);
>> +     sndif_cleanup_virt_devices(info);
>> +}
>> +
>> +/*
>> + * Invoked when the backend is finally 'ready' (and has told produced
>> + * the details about the physical device - #sectors, size, etc).
>> + */
>> +static void sndfront_connect(struct sndfront_info *info)
>> +{
>> +     unsigned int stream_id;
>> +     int err;
>> +
>> +     switch (info->connected) {
>> +     case SNDIF_STATE_CONNECTED:
>> +
>> +             /* fall through */
>> +     case SNDIF_STATE_SUSPENDED:
>> +             return;
>> +
>> +     default:
>> +             break;
>> +     }
>> +
>> +     dev_dbg(&info->xbdev->dev, "%s:%s.\n",
>> +             __func__, info->xbdev->otherend);
>> +
>> +     xenbus_switch_state(info->xbdev, XenbusStateConnected);
>> +
>> +     err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "stream_id", "%u",
>> +                         &stream_id, NULL);
>> +     if (err)
>> +             return;
>> +
>> +     err = sndif_add_virt_devices(info, stream_id);
>> +     if (err)
>> +             return;
>> +
>> +     spin_lock_irq(&info->io_lock);
>> +     info->connected = SNDIF_STATE_CONNECTED;
>> +     spin_unlock_irq(&info->io_lock);
>> +}
>> +
>> +/**
>> + * Callback received when the backend's state changes.
>> + */
>> +static void sndback_changed(struct xenbus_device *dev,
>> +                         enum xenbus_state backend_state)
>> +{
>> +     struct sndfront_info *info = dev_get_drvdata(&dev->dev);
>> +
>> +     dev_dbg(&dev->dev, "sndfront:sndback_changed to state %d.\n",
>> +             backend_state);
>> +
>> +     switch (backend_state) {
>> +     case XenbusStateInitialising:
>> +     case XenbusStateInitWait:
>> +     case XenbusStateInitialised:
>> +     case XenbusStateReconfiguring:
>> +     case XenbusStateReconfigured:
>> +     case XenbusStateUnknown:
>> +     case XenbusStateClosed:
>> +             break;
>> +
>> +     case XenbusStateConnected:
>> +             sndfront_connect(info);
>> +             break;
>> +
>> +     case XenbusStateClosing:
>> +             sndfront_closing(info);
>> +             break;
>> +     }
>> +}
>
> We defenitely need a doc to describe how the state changes are supposed
> to occur with this protocol (each of the other protocols are subtly
> different in this regard unfortunately).
I'll do this doc a bit later.

>
>> +static int sndfront_remove(struct xenbus_device *xbdev)
>> +{
>> +     struct sndfront_info *info = dev_get_drvdata(&xbdev->dev);
>> +
>> +     dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
>> +
>> +     platform_device_unregister(info->card_dev);
>> +
>> +     sndif_free(info, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct xenbus_device_id xen_snd_ids[] = {
>> +     { "vsnd" },
>> +     { "" }
>> +};
>> +
>> +static struct xenbus_driver xen_snd_driver = {
>> +     .name = "xensnd",
>> +     .ids = xen_snd_ids,
>> +     .probe = sndfront_probe,
>> +     .remove = sndfront_remove,
>> +     .resume = sndfront_resume,
>> +     .otherend_changed = sndback_changed,
>> +};
>> +
>> +static int __init xen_snd_front_init(void)
>> +{
>> +     int ret = 0;
>> +
>> +     /*FIXME: xen_pv_domain() should be here, but ARM hardcoded to hvm*/
>> +     if (!xen_domain())
>> +             return -ENODEV;
>> +
>> +     /* Nothing to do if running in dom0. */
>> +     if (xen_initial_domain())
>> +             return -ENODEV;
>> +
>> +     ret = xenbus_register_frontend(&xen_snd_driver);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = platform_driver_register(&snd_virtualcard_driver);
>
> Shouldn't this be called from sndfront_probe (similarly to what other
> Xen PV frontend do)?
Currently we have 2 virtualized streams for playback and capture.
Each stream registered using his own sndfront_probe function.
But virtual sound card driver should registered once.
So at this moment this is the best place for this function IMHO.

>
>> +     return ret;
>> +}
>> +
>> +static void __exit xen_snd_front_cleanup(void)
>> +{
>> +     xenbus_unregister_driver(&xen_snd_driver);
>> +     platform_driver_unregister(&snd_virtualcard_driver);
>> +}
>> +
>> +module_init(xen_snd_front_init);
>> +module_exit(xen_snd_front_cleanup);
>> +
>> +MODULE_DESCRIPTION("Xen virtual audio device frontend");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("xen:vsnd");
>> --
>> 1.9.1
>>
>>
>> _______________________________________________
>> Xen-devel mailing list
>> Xen-devel@xxxxxxxxxxxxx
>> http://lists.xen.org/xen-devel
>>

_______________________________________________
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®.