|
[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 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.
> 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.
> 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?
> + 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_*.
> + 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.
> + 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).
> +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)?
> + 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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |