[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |