[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 2/3] xen-sndfront: switch to the v2 driver
From: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx> Now this driver registers an virtual sound card and sends an PCM streams to the backend driver. Backend driver is an user-space application and uses ALSA with dmix plugin to play audio. Signed-off-by: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx> Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx> --- include/xen/interface/io/sndif.h | 41 +- sound/drivers/Kconfig | 3 +- sound/drivers/xen-sndfront.c | 1362 ++++++++++++++++++++++++-------------- 3 files changed, 873 insertions(+), 533 deletions(-) diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h index 9ef0c41..2fae4df 100644 --- a/include/xen/interface/io/sndif.h +++ b/include/xen/interface/io/sndif.h @@ -19,10 +19,6 @@ #define SNDIF_OP_WRITE 3 #define SNDIF_OP_IOCTL 4 -#define SNDIF_DEV_TYPE_CONTROL 0 -#define SNDIF_DEV_TYPE_STREAM_PLAY 1 -#define SNDIF_DEV_TYPE_STREAM_CAPTURE 2 - #define SNDIF_MAX_PAGES_PER_REQUEST 10 #define SNDIF_DEV_ID_CNT 5 @@ -35,53 +31,38 @@ /* Operation completed successfully. */ #define SNDIF_RSP_OKAY 0 +struct alsa_hwparams { + snd_pcm_format_t format; + unsigned int channels; + unsigned int rate; +}; + struct sndif_request_open { - unsigned int dev_num; - unsigned int card_num; - unsigned int dev_type; + struct alsa_hwparams hwparams; unsigned int _pad1; uint64_t id; /* private guest value, echoed in resp */ unsigned int _pad2; - unsigned int _pad3; -} __attribute__((__packed__)); - -struct sndif_request_ioctl { - unsigned int dev_num; - unsigned int card_num; - unsigned int dev_type; - unsigned int cmd; - uint64_t id; /* private guest value, echoed in resp */ - unsigned int add_len_to; - unsigned int add_len_from; - grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST]; } __attribute__((__packed__)); struct sndif_request_rw { - unsigned int dev_num; - unsigned int card_num; - unsigned int dev_type; - unsigned int _pad1; + struct alsa_hwparams _pad1; + unsigned int _pad2; uint64_t id; /* private guest value, echoed in resp */ unsigned int len; - unsigned int is_write; grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST]; } __attribute__((__packed__)); struct sndif_request_common { - unsigned int dev_num; + struct alsa_hwparams _pad1; unsigned int _pad2; - unsigned int dev_type; - unsigned int _pad3; uint64_t id; /* private guest value, echoed in resp */ - unsigned int _pad4; - unsigned int _pad5; + unsigned int _pad3; } __attribute__((__packed__)); struct sndif_request { uint8_t operation; /* SNDIF_OP_??? */ union { struct sndif_request_open open; - struct sndif_request_ioctl ioctl; struct sndif_request_rw rw; struct sndif_request_common common; } u; diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 7364679..cd3db5a 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -28,8 +28,9 @@ config XEN_SND_FRONTEND tristate "Xen virtual audio front-end driver support" depends on SND && XEN_DOMU default n + select SND_PCM help - This driver implements the back-end of the Xen virtual + This driver implements the front-end of the Xen virtual audio driver. It communicates with a back-end in another domain. diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c index c7f8827..da048fc 100644 --- a/sound/drivers/xen-sndfront.c +++ b/sound/drivers/xen-sndfront.c @@ -23,16 +23,26 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/jiffies.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/hrtimer.h> +#include <linux/math64.h> #include <linux/module.h> -#include <linux/errno.h> -#include <linux/cdev.h> -#include <linux/fs.h> -#include <linux/stddef.h> #include <linux/vmalloc.h> -#include <linux/delay.h> -#include <linux/uaccess.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/tlv.h> +#include <sound/pcm.h> +#include <sound/rawmidi.h> +#include <sound/info.h> +#include <sound/initval.h> -#include <sound/asound.h> +#include <linux/uaccess.h> #include <xen/xen.h> #include <xen/events.h> @@ -44,101 +54,168 @@ #include <xen/interface/io/protocols.h> #include <xen/interface/io/sndif.h> -#define VSND_MAJOR 160 -#define VSND_MINOR_CTRL 0 -#define VSND_MINOR_STREAM 32 - -#define VSND_WAIT_ANSWER_TOUT 5000 - -enum sndif_state { - SNDIF_STATE_DISCONNECTED, - SNDIF_STATE_CONNECTED, - SNDIF_STATE_SUSPENDED, -}; - -struct vsnd_stream { - dev_t dev; - struct vsnd_card *card; - unsigned int sample_bits; - unsigned int channels; - unsigned int stream_p; -}; - -struct vsnd_ctrl { - dev_t dev; - struct vsnd_card *card; -}; +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}"); + +#define VSND_WAIT_ANSWER_TOUT 5000 + +#define MAX_PCM_DEVICES 1 +#define MAX_PCM_SUBSTREAMS 1 +#define MAX_BUFFER_SIZE (64*1024) + +/* defaults */ +#define MIN_PERIOD_SIZE 64 +#define MAX_PERIOD_SIZE (4*1024) +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS |\ + SNDRV_PCM_RATE_8000_48000) +#define USE_RATE_MIN 5500 +#define USE_RATE_MAX 48000 +#define USE_CHANNELS_MIN 1 +#define USE_CHANNELS_MAX 2 +#define USE_PERIODS_MIN 1 +#define USE_PERIODS_MAX 1024 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL}; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for virtual soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for virtual soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable this virtual soundcard."); +module_param_array(model, charp, NULL, 0444); +MODULE_PARM_DESC(model, "Soundcard model."); + +#define MIXER_ADDR_MASTER_IN 0 +#define MIXER_ADDR_MASTER_OUT 1 +#define MIXER_ADDR_LAST MIXER_ADDR_MASTER_OUT struct vsnd_card { struct sndfront_info *fr_info; - unsigned int card_num; - unsigned int dev_num; - unsigned int dev_type; unsigned int dev_id; - struct cdev cdev; - struct vsnd_stream *vstream; - struct vsnd_ctrl *vctrl; grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST]; unsigned char *buf; }; #define SND_RING_SIZE __CONST_RING_SIZE(sndif, PAGE_SIZE) +enum sndif_state { + SNDIF_STATE_DISCONNECTED, + SNDIF_STATE_CONNECTED, + SNDIF_STATE_SUSPENDED, +}; + struct sndfront_info { - struct mutex mutex; /* protect sndfront closing state */ - struct mutex tmp_fops_mutex; /* protect file operations */ + struct mutex mutex; struct completion completion; - spinlock_t io_lock; /* protect 'connected' member */ + spinlock_t io_lock; struct xenbus_device *xbdev; enum sndif_state connected; int ring_ref; struct sndif_front_ring ring; unsigned int evtchn, irq; struct vsnd_card *vcard; - struct class *vsndcl; int bret_code; - atomic_t file_refcnt; + struct platform_device *card_dev; }; -static struct class *vsndclass; +#define GRANT_INVALID_REF 0 -static const struct file_operations vsndcore_fops = { - .owner = THIS_MODULE, +struct virtualcard_model { + const char *name; + u64 formats; + size_t buffer_bytes_max; + size_t period_bytes_min; + size_t period_bytes_max; + unsigned int periods_min; + unsigned int periods_max; + unsigned int rates; + unsigned int rate_min; + unsigned int rate_max; + unsigned int channels_min; + unsigned int channels_max; }; -#define GRANT_INVALID_REF 0 +struct stream_info { + snd_pcm_uframes_t position; /* Current position */ + snd_pcm_uframes_t crossed; /* Number of crossed writes*/ + snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; /* channels */ + bool opened; /* opened status */ +}; -static unsigned long vmalloc_to_mfn(void *address) -{ - return pfn_to_mfn(vmalloc_to_pfn(address)); -} +struct snd_virtualcard { + struct snd_card *card; + struct virtualcard_model *model; + struct snd_pcm *pcm; + struct snd_pcm_hardware pcm_hw; + spinlock_t mixer_lock; /* protect mixer settings */ + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source[MIXER_ADDR_LAST+1][2]; + struct sndfront_info *fr_info; + struct stream_info streams[2]; +}; -static char *vsound_devnode(struct device *dev, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "vsnd/%s", dev_name(dev)); -} +/* + * card models + */ + +struct virtualcard_model model_ac97 = { + .name = "ac97", + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, +}; + +struct virtualcard_model *virtualcard_models[] = { + &model_ac97, + NULL +}; + +/* + * PCM interface + */ + +static struct snd_pcm_hardware virtualcard_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = MIN_PERIOD_SIZE, + .period_bytes_max = MAX_PERIOD_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; static inline -struct snd_interval *pcm_param_to_interval(struct snd_pcm_hw_params *p, int n) -{ - return &p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard, + struct snd_pcm_substream *substream) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &virtualcard->streams[0]; + else + return &virtualcard->streams[1]; } -static inline int pcm_param_is_interval(int p) -{ - return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && - (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); -} -static unsigned int pcm_param_get_int(struct snd_pcm_hw_params *p, int n) +static unsigned long vmalloc_to_mfn(void *address) { - if (pcm_param_is_interval(n)) { - struct snd_interval *i = pcm_param_to_interval(p, n); - - if (i->integer) - return i->max; - } - return 0; + return pfn_to_mfn(vmalloc_to_pfn(address)); } static inline void flush_requests(struct sndfront_info *info) @@ -151,7 +228,10 @@ static inline void flush_requests(struct sndfront_info *info) notify_remote_via_irq(info->irq); } -static int sndif_queue_request_open(struct sndfront_info *info) +static int sndif_queue_request_open(struct sndfront_info *info, + snd_pcm_format_t format, + unsigned int channels, + unsigned int rate) { struct sndif_request *req; @@ -161,11 +241,10 @@ static int sndif_queue_request_open(struct sndfront_info *info) req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); req->operation = SNDIF_OP_OPEN; - req->u.open.dev_type = info->vcard->dev_type; - req->u.open.dev_num = info->vcard->dev_num; - req->u.open.card_num = info->vcard->card_num; req->u.open.id = info->vcard->dev_id; - + req->u.open.hwparams.format = format; + req->u.open.hwparams.channels = channels; + req->u.open.hwparams.rate = rate; info->ring.req_prod_pvt++; flush_requests(info); @@ -182,9 +261,6 @@ static int sndif_queue_request_close(struct sndfront_info *info) req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); req->operation = SNDIF_OP_CLOSE; - req->u.open.dev_type = info->vcard->dev_type; - req->u.open.dev_num = info->vcard->dev_num; - req->u.open.card_num = info->vcard->card_num; req->u.open.id = info->vcard->dev_id; info->ring.req_prod_pvt++; @@ -193,10 +269,8 @@ static int sndif_queue_request_close(struct sndfront_info *info) return 0; } -static int sndif_queue_request_ioctl(struct sndfront_info *info, - unsigned int cmd, - unsigned int add_len_to, - unsigned int add_len_from) +static int sndif_queue_request_write(struct sndfront_info *info, + unsigned int len) { struct sndif_request *req; grant_ref_t *gref; @@ -207,20 +281,15 @@ static int sndif_queue_request_ioctl(struct sndfront_info *info, req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); - req->operation = SNDIF_OP_IOCTL; - req->u.ioctl.dev_type = info->vcard->dev_type; - req->u.ioctl.dev_num = info->vcard->dev_num; - req->u.ioctl.card_num = info->vcard->card_num; - req->u.ioctl.id = info->vcard->dev_id; + req->operation = SNDIF_OP_WRITE; + req->u.rw.id = info->vcard->dev_id; - req->u.ioctl.cmd = cmd; - req->u.ioctl.add_len_to = add_len_to; - req->u.ioctl.add_len_from = add_len_from; + req->u.rw.len = len; gref = info->vcard->grefs; for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) - req->u.ioctl.gref[i] = gref[i]; + req->u.rw.gref[i] = gref[i]; info->ring.req_prod_pvt++; @@ -228,361 +297,112 @@ static int sndif_queue_request_ioctl(struct sndfront_info *info, return 0; } -static int vsnd_file_open(struct inode *inode, struct file *file) +static int alsa_pcm_open(struct sndfront_info *info, + snd_pcm_format_t format, + unsigned int channels, + unsigned int rate) { - struct sndfront_info *info; - struct vsnd_card *vcard; unsigned long answer_tout; - int ret = 0; - - vcard = container_of(inode->i_cdev, struct vsnd_card, cdev); - info = vcard->fr_info; - - mutex_lock(&info->tmp_fops_mutex); - file->private_data = info; - - if (atomic_inc_return(&info->file_refcnt) > 1) - goto end_fops_handler; reinit_completion(&info->completion); - if (sndif_queue_request_open(info)) { - ret = -EIO; - goto end_fops_handler; - } + if (sndif_queue_request_open(info, format, channels, rate)) + return -EIO; answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); if (wait_for_completion_interruptible_timeout(&info->completion, - answer_tout) <= 0) { - ret = -ETIMEDOUT; - goto end_fops_handler; - } - - ret = info->bret_code; + answer_tout) <= 0) + return -ETIMEDOUT; -end_fops_handler: - mutex_unlock(&info->tmp_fops_mutex); - return ret; + return info->bret_code; } -static int vsnd_file_release(struct inode *inode, struct file *file) +static int alsa_pcm_close(struct sndfront_info *info) { - struct sndfront_info *info; unsigned long answer_tout; - int ret = 0; - - info = file->private_data; - - if (!info) - return -EINVAL; - - mutex_lock(&info->tmp_fops_mutex); - if (atomic_dec_return(&info->file_refcnt) > 0) - goto end_fops_handler; reinit_completion(&info->completion); - if (sndif_queue_request_close(info)) { - ret = -EIO; - goto end_fops_handler; - } + if (sndif_queue_request_close(info)) + return -EIO; answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); if (wait_for_completion_interruptible_timeout(&info->completion, - answer_tout) <= 0) { - ret = -ETIMEDOUT; - goto end_fops_handler; - } - - ret = info->bret_code; + answer_tout) <= 0) + return -ETIMEDOUT; -end_fops_handler: - mutex_unlock(&info->tmp_fops_mutex); - return ret; + return info->bret_code; } -static -long vsnd_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int alsa_pcm_write(struct sndfront_info *info, char __user *buf, + int len) { unsigned char *shared_data; - struct sndfront_info *info; - int datalen = 0; - int ret = 0; - int ioctl_dir; - unsigned int add_len_to = 0; - unsigned int frames; - struct snd_pcm_hw_params *hw_params; - struct snd_xferi *esnd_xferi; - struct vsnd_stream *vstream; - struct snd_ctl_elem_list *elist = NULL; - unsigned int elist_cnt = 0; - unsigned int add_len_from = 0; unsigned long answer_tout; - info = file->private_data; - - if (!info) - return -EINVAL; - - mutex_lock(&info->tmp_fops_mutex); - shared_data = info->vcard->buf; - ioctl_dir = (cmd >> _IOC_DIRSHIFT) & _IOC_DIRMASK; - datalen = (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK; + if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) + return -EFAULT; - if (datalen > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) { - ret = -EFAULT; - goto end_fops_handler; - } - - if (datalen && (ioctl_dir & _IOC_WRITE)) { - if (copy_from_user(shared_data, (void __user *)arg, datalen)) { - ret = -EIO; - goto end_fops_handler; - } - /* Wait data to be visible to the other end */ - wmb(); - } - - switch (cmd) { - case SNDRV_PCM_IOCTL_WRITEI_FRAMES: - esnd_xferi = (struct snd_xferi *)shared_data; - vstream = info->vcard->vstream; - frames = esnd_xferi->frames; - add_len_to = vstream->channels * vstream->sample_bits / 8; - add_len_to *= frames; - if (add_len_to > - PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) { - ret = -EFAULT; - goto end_fops_handler; - } - - if (copy_from_user(shared_data + datalen, - esnd_xferi->buf, add_len_to)) { - ret = -EIO; - goto end_fops_handler; - } - /* Wait data to be visible to the other end */ - wmb(); - break; - - case SNDRV_CTL_IOCTL_ELEM_LIST: - elist = (struct snd_ctl_elem_list *)shared_data; - elist_cnt = elist->count; - - if (!elist_cnt) - break; - - add_len_from = elist_cnt * sizeof(struct snd_ctl_elem_id); - - break; - } - - if (add_len_from > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST - datalen) { - ret = -EFAULT; - goto end_fops_handler; - } + if (copy_from_user(shared_data, buf, len)) + return -EFAULT; reinit_completion(&info->completion); - if (sndif_queue_request_ioctl(info, cmd, add_len_to, 0)) { - ret = -EIO; - goto end_fops_handler; - } + if (sndif_queue_request_write(info, len)) + return -EIO; answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); if (wait_for_completion_interruptible_timeout(&info->completion, - answer_tout) <= 0) { - ret = -ETIMEDOUT; - goto end_fops_handler; - } - - ret = info->bret_code; - - if (ret) - goto end_fops_handler; - - if (datalen && (ioctl_dir & _IOC_READ)) { - /* Wait data to be available from the other end */ - rmb(); - if (copy_to_user((void __user *)arg, shared_data, datalen)) { - ret = -EIO; - goto end_fops_handler; - } - } - - switch (cmd) { - case SNDRV_PCM_IOCTL_HW_PARAMS: - /* Get PCM hw parameters */ - hw_params = (struct snd_pcm_hw_params *)shared_data; - vstream = info->vcard->vstream; - vstream->channels = - pcm_param_get_int(hw_params, - SNDRV_PCM_HW_PARAM_CHANNELS); - vstream->sample_bits = - pcm_param_get_int(hw_params, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - break; - - case SNDRV_CTL_IOCTL_ELEM_LIST: - /* copy extra data from additional pages */ - if (!add_len_from) - break; + answer_tout) <= 0) + return -ETIMEDOUT; - /* Wait data to be available from the other end */ - rmb(); - if (copy_to_user((void __user *)elist->pids, - shared_data + datalen, add_len_from)) { - ret = -EIO; - goto end_fops_handler; - } - break; - } - -end_fops_handler: - mutex_unlock(&info->tmp_fops_mutex); - return ret; + return info->bret_code; } -static const struct file_operations vsnd_file_ops = { - .open = vsnd_file_open, - .release = vsnd_file_release, - .unlocked_ioctl = vsnd_file_ioctl, - .owner = THIS_MODULE, -}; - -static struct vsnd_ctrl *sndif_allocate_vctrl(struct sndfront_info *info) +static int alsa_pcm_silence(struct sndfront_info *info, int len) { - struct vsnd_ctrl *vctrl; - unsigned int card_num; - grant_ref_t gref_head; - unsigned long mfn; - int ref; - int i; - - vctrl = kmalloc(sizeof(*vctrl), GFP_KERNEL); - - if (!vctrl) - goto err_ret; - - card_num = info->vcard->card_num; - - vctrl->card = info->vcard; - - vctrl->dev = MKDEV(VSND_MAJOR, VSND_MINOR_CTRL); - if (device_create(info->vsndcl, NULL, vctrl->dev, NULL, - "controlC%u", card_num) == NULL) - goto err_free_vctrl; - - cdev_init(&info->vcard->cdev, &vsnd_file_ops); - - if (cdev_add(&info->vcard->cdev, vctrl->dev, 1) < 0) - goto err_vctrl_dev_destroy; - - info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE); - if (!info->vcard->buf) - goto err_vctrl_cdev_del; - - if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST, - &gref_head)) - goto err_vctrl_free_buf; - - for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) { - ref = gnttab_claim_grant_reference(&gref_head); - BUG_ON(ref == -ENOSPC); - - mfn = vmalloc_to_mfn(info->vcard->buf + PAGE_SIZE * i); + unsigned char *shared_data; + unsigned long answer_tout; - gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, - mfn, 0); + shared_data = info->vcard->buf; - info->vcard->grefs[i] = ref; - } + if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) + return -EFAULT; - gnttab_free_grant_references(gref_head); - return vctrl; + memset(shared_data, 0, len); -err_vctrl_free_buf: - vfree(info->vcard->buf); - for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) - gnttab_end_foreign_access(info->vcard->grefs[i], 0, 0UL); + reinit_completion(&info->completion); - gnttab_free_grant_references(gref_head); -err_vctrl_cdev_del: - cdev_del(&info->vcard->cdev); -err_vctrl_dev_destroy: - device_destroy(info->vsndcl, vctrl->dev); -err_free_vctrl: - kfree(vctrl); -err_ret: - return vctrl; -} + if (sndif_queue_request_write(info, len)) + return -EIO; -static void sndif_free_vctrl(struct sndfront_info *info) -{ - int i; - struct vsnd_card *vcard = info->vcard; - struct vsnd_ctrl *vctrl = vcard->vctrl; - - if (vctrl) { - vfree(info->vcard->buf); - for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) - gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL); + answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); + if (wait_for_completion_interruptible_timeout(&info->completion, + answer_tout) <= 0) + return -ETIMEDOUT; - cdev_del(&info->vcard->cdev); - device_destroy(info->vsndcl, vctrl->dev); - kfree(vctrl); - } + return info->bret_code; } -static struct vsnd_stream *sndif_allocate_vstream(struct sndfront_info *info) +static int sndif_setup_vcard(struct sndfront_info *info) { - struct vsnd_stream *vstream; - unsigned int card_num; - unsigned int dev_num; - unsigned int dev_minor; grant_ref_t gref_head; unsigned long mfn; int ref; int i; - - vstream = kmalloc(sizeof(*vstream), GFP_KERNEL); - - if (!vstream) - goto err_ret; - - card_num = info->vcard->card_num; - dev_num = info->vcard->dev_num; - vstream->card = info->vcard; - - if (info->vcard->dev_type == SNDIF_DEV_TYPE_STREAM_PLAY) - vstream->stream_p = 1; - else - vstream->stream_p = 0; - - /* set default parameters */ - vstream->channels = 2; - vstream->sample_bits = 16; - - dev_minor = VSND_MINOR_STREAM + dev_num + 20 * (!!vstream->stream_p); - vstream->dev = MKDEV(VSND_MAJOR, dev_minor); - if (device_create(info->vsndcl, NULL, vstream->dev, NULL, - "pcmC%uD%u%c", card_num, dev_num, - vstream->stream_p ? 'p' : 'c') == NULL) - goto err_free_vstream; - - cdev_init(&info->vcard->cdev, &vsnd_file_ops); - - if (cdev_add(&info->vcard->cdev, vstream->dev, 1) < 0) - goto err_vstream_dev_destroy; + int ret; info->vcard->buf = vmalloc(SNDIF_MAX_PAGES_PER_REQUEST * PAGE_SIZE); - if (!info->vcard->buf) - goto err_vstream_cdev_del; + if (!info->vcard->buf) { + ret = -ENOMEM; + goto err_ret; + } - if (gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST, - &gref_head)) + ret = gnttab_alloc_grant_references(SNDIF_MAX_PAGES_PER_REQUEST, + &gref_head); + if (ret) goto err_vstream_free_buf; for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) { @@ -598,7 +418,7 @@ static struct vsnd_stream *sndif_allocate_vstream(struct sndfront_info *info) } gnttab_free_grant_references(gref_head); - return vstream; + return ret; err_vstream_free_buf: vfree(info->vcard->buf); @@ -606,100 +426,48 @@ err_vstream_free_buf: gnttab_end_foreign_access(info->vcard->grefs[i], 0, 0UL); gnttab_free_grant_references(gref_head); -err_vstream_cdev_del: - cdev_del(&info->vcard->cdev); -err_vstream_dev_destroy: - device_destroy(info->vsndcl, vstream->dev); -err_free_vstream: - kfree(vstream); err_ret: - return vstream; + return ret; } -static void sndif_free_vstream(struct sndfront_info *info) +static void sndif_cleanup_vcard(struct sndfront_info *info) { int i; - struct vsnd_card *vcard = info->vcard; - struct vsnd_stream *vstream = vcard->vstream; - if (vstream) { + if (info->vcard->buf) { vfree(info->vcard->buf); for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) - gnttab_end_foreign_access(vcard->grefs[i], 0, 0UL); - - cdev_del(&info->vcard->cdev); - device_destroy(info->vsndcl, vstream->dev); - kfree(vstream); + gnttab_end_foreign_access(info->vcard->grefs[i], + 0, 0UL); } } static int sndif_add_virt_devices(struct sndfront_info *info, - unsigned int dev_type, - unsigned int dev_num, - unsigned int card_num, unsigned int dev_id) { - int ret; + int ret = 0; struct vsnd_card *vcard; - struct vsnd_ctrl *vctrl = NULL; - struct vsnd_stream *vstream = NULL; vcard = kmalloc(sizeof(*vcard), GFP_KERNEL); if (!vcard) return -ENOMEM; - info->vsndcl = vsndclass; - - vcard->card_num = card_num; - vcard->dev_num = dev_num; - vcard->dev_type = dev_type; vcard->dev_id = dev_id; vcard->fr_info = info; info->vcard = vcard; - switch (dev_type) { - case SNDIF_DEV_TYPE_CONTROL: - vctrl = sndif_allocate_vctrl(info); - - if (!vctrl) { - ret = -ENOMEM; - goto err_free_vcard; - } - break; - - case SNDIF_DEV_TYPE_STREAM_PLAY: - case SNDIF_DEV_TYPE_STREAM_CAPTURE: - vstream = sndif_allocate_vstream(info); - - if (!vstream) { - ret = -ENOMEM; - goto err_free_vcard; - } - break; - - default: - ret = -EFAULT; - goto err_free_vcard; - } + sndif_setup_vcard(info); - info->vcard->vctrl = vctrl; - info->vcard->vstream = vstream; - - return ret; - -err_free_vcard: - kfree(info->vcard); return ret; } static void sndif_cleanup_virt_devices(struct sndfront_info *info) { if (info->vcard) { - sndif_free_vstream(info); - sndif_free_vctrl(info); + sndif_cleanup_vcard(info); kfree(info->vcard); } } @@ -748,7 +516,7 @@ static irqreturn_t sndif_interrupt(int irq, void *data) switch (bret->operation) { case SNDIF_OP_OPEN: case SNDIF_OP_CLOSE: - case SNDIF_OP_IOCTL: + case SNDIF_OP_WRITE: if (unlikely(bret->status != SNDIF_RSP_OKAY)) dev_dbg(&info->xbdev->dev, "snddev data request error: %x\n", @@ -877,6 +645,620 @@ again: return err; } +static int virtualcard_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + runtime->stop_threshold = runtime->buffer_size + 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + runtime->stop_threshold = runtime->buffer_size; + break; + } + return 0; +} + +static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream) +{ + int err; + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_info *vcard_stream; + + vcard_stream = get_vcard_stream(virtualcard, substream); + + if ((runtime->rate != vcard_stream->rate) || + (runtime->channels != vcard_stream->channels) || + (runtime->format != vcard_stream->format)) { + if (vcard_stream->opened) { + err = alsa_pcm_close(virtualcard->fr_info); + if (err) + return err; + + /* if closed successfully */ + vcard_stream->opened = false; + } + err = alsa_pcm_open(virtualcard->fr_info, runtime->format, + runtime->channels, runtime->rate); + if (err) + return err; + + /* if opened successfully */ + vcard_stream->rate = runtime->rate; + vcard_stream->channels = runtime->channels; + vcard_stream->format = runtime->format; + vcard_stream->opened = true; + } + return 0; +} + +static +snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + snd_pcm_uframes_t buff_size = substream->runtime->buffer_size; + struct stream_info *vcard_stream; + + vcard_stream = get_vcard_stream(virtualcard, substream); + + if (vcard_stream->crossed) { + snd_pcm_uframes_t hw_base = substream->runtime->hw_ptr_base; + + hw_base += buff_size; + if (hw_base >= substream->runtime->boundary) + hw_base = 0; + + substream->runtime->hw_ptr_base = hw_base; + vcard_stream->crossed = 0; + } + return vcard_stream->position; +} + +static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return 0; +} + +static int virtualcard_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int virtualcard_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_info *vcard_stream; + + vcard_stream = get_vcard_stream(virtualcard, substream); + + vcard_stream->channels = 0; + vcard_stream->rate = 0; + vcard_stream->position = 0; + vcard_stream->crossed = 0; + vcard_stream->format = 0; + vcard_stream->opened = false; + + runtime->hw = virtualcard->pcm_hw; + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE); + runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED; + return 0; +} + +static int virtualcard_pcm_close(struct snd_pcm_substream *substream) +{ + int err; + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + + err = alsa_pcm_close(virtualcard->fr_info); + if (err) + return err; + + get_vcard_stream(virtualcard, substream)->opened = false; + + return 0; +} + +static int virtualcard_pcm_playback_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + void __user *src, + snd_pcm_uframes_t count) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_info *vcard_stream = &virtualcard->streams[0]; + snd_pcm_uframes_t stream_pos = vcard_stream->position; + + vcard_stream->position = (stream_pos + count) % runtime->buffer_size; + vcard_stream->crossed = count / runtime->buffer_size; + return alsa_pcm_write(virtualcard->fr_info, src, + frames_to_bytes(runtime, count)); +} + +static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_info *vcard_stream = &virtualcard->streams[0]; + snd_pcm_uframes_t stream_pos = vcard_stream->position; + + vcard_stream->position = (stream_pos + count) % runtime->buffer_size; + vcard_stream->crossed = count / runtime->buffer_size; + + return alsa_pcm_silence(virtualcard->fr_info, + frames_to_bytes(runtime, count)); +} + +static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + void __user *dst, + snd_pcm_uframes_t count) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_info *vcard_stream = &virtualcard->streams[1]; + snd_pcm_uframes_t stream_pos = vcard_stream->position; + + vcard_stream->position = (stream_pos + count) % runtime->buffer_size; + vcard_stream->crossed = count / runtime->buffer_size; + + return 0; +} + +static struct snd_pcm_ops virtualcard_pcm_playback_ops = { + .open = virtualcard_pcm_open, + .close = virtualcard_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = virtualcard_pcm_hw_params, + .hw_free = virtualcard_pcm_hw_free, + .prepare = virtualcard_pcm_prepare, + .trigger = virtualcard_pcm_trigger, + .pointer = virtualcard_pcm_pointer, + .copy = virtualcard_pcm_playback_copy, + .silence = virtualcard_pcm_playback_silence, +}; + +static struct snd_pcm_ops virtualcard_pcm_capture_ops = { + .open = virtualcard_pcm_open, + .close = virtualcard_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = virtualcard_pcm_hw_params, + .hw_free = virtualcard_pcm_hw_free, + .prepare = virtualcard_pcm_prepare, + .trigger = virtualcard_pcm_trigger, + .pointer = virtualcard_pcm_pointer, + .copy = virtualcard_pcm_capture_copy, +}; + +static int snd_card_virtualcard_pcm(struct snd_virtualcard *virtualcard, + int device, int substreams) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(virtualcard->card, "Virtual card PCM", device, + substreams, substreams, &pcm); + if (err < 0) + return err; + virtualcard->pcm = pcm; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &virtualcard_pcm_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &virtualcard_pcm_capture_ops); + pcm->private_data = virtualcard; + pcm->info_flags = 0; + strcpy(pcm->name, "Virtual card PCM"); + + return 0; +} + +/* + * mixer interface + */ + +#define VIRTUALCARD_VOLUME(xname, xindex, addr) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, \ + .index = xindex, \ + .info = snd_virtualcard_volume_info, \ + .get = snd_virtualcard_volume_get, \ + .put = snd_virtualcard_volume_put, \ + .private_value = addr, \ + .tlv = { .p = db_scale_virtualcard } \ +} + +static int snd_virtualcard_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = -50; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_virtualcard_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + spin_lock_irq(&virtualcard->mixer_lock); + ucontrol->value.integer.value[0] = virtualcard->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = virtualcard->mixer_volume[addr][1]; + spin_unlock_irq(&virtualcard->mixer_lock); + return 0; +} + +static int snd_virtualcard_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0]; + if (left < -50) + left = -50; + if (left > 100) + left = 100; + right = ucontrol->value.integer.value[1]; + if (right < -50) + right = -50; + if (right > 100) + right = 100; + spin_lock_irq(&virtualcard->mixer_lock); + change = virtualcard->mixer_volume[addr][0] != left || + virtualcard->mixer_volume[addr][1] != right; + virtualcard->mixer_volume[addr][0] = left; + virtualcard->mixer_volume[addr][1] = right; + spin_unlock_irq(&virtualcard->mixer_lock); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_virtualcard, -4500, 30, 0); + +#define VIRTUALCARD_CAPSRC(xname, xindex, addr) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_virtualcard_capsrc_info, \ + .get = snd_virtualcard_capsrc_get, \ + .put = snd_virtualcard_capsrc_put, \ + .private_value = addr \ +} + +#define snd_virtualcard_capsrc_info snd_ctl_boolean_stereo_info + +static int snd_virtualcard_capsrc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + spin_lock_irq(&virtualcard->mixer_lock); + ucontrol->value.integer.value[0] = virtualcard->capture_source[addr][0]; + ucontrol->value.integer.value[1] = virtualcard->capture_source[addr][1]; + spin_unlock_irq(&virtualcard->mixer_lock); + return 0; +} + +static int snd_virtualcard_capsrc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_virtualcard *virtualcard = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + spin_lock_irq(&virtualcard->mixer_lock); + change = virtualcard->capture_source[addr][0] != left && + virtualcard->capture_source[addr][1] != right; + virtualcard->capture_source[addr][0] = left; + virtualcard->capture_source[addr][1] = right; + spin_unlock_irq(&virtualcard->mixer_lock); + return change; +} + +static struct snd_kcontrol_new snd_virtualcard_controls[] = { +VIRTUALCARD_VOLUME("Master Out Volume", 0, MIXER_ADDR_MASTER_OUT), +VIRTUALCARD_CAPSRC("Master Out Switch", 0, MIXER_ADDR_MASTER_OUT), +VIRTUALCARD_VOLUME("Master In Volume", 0, MIXER_ADDR_MASTER_IN), +VIRTUALCARD_CAPSRC("Master In Switch", 0, MIXER_ADDR_MASTER_IN), +}; + +static int snd_card_virtualcard_new_mixer(struct snd_virtualcard *virtualcard) +{ + struct snd_card *card = virtualcard->card; + struct snd_kcontrol *kcontrol; + unsigned int idx; + int err; + + spin_lock_init(&virtualcard->mixer_lock); + strcpy(card->mixername, "Virtual card Mixer"); + + for (idx = 0; idx < ARRAY_SIZE(snd_virtualcard_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_virtualcard_controls[idx], + virtualcard); + err = snd_ctl_add(card, kcontrol); + if (err < 0) + return err; + } + return 0; +} + +/* #define CONFIG_SND_DEBUG */ +/* #define CONFIG_PROC_FS */ +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS) +/* + * proc interface + */ +static void print_formats(struct snd_virtualcard *virtualcard, + struct snd_info_buffer *buffer) +{ + int i; + + for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + if (virtualcard->pcm_hw.formats & (1ULL << i)) + snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); + } +} + +static void print_rates(struct snd_virtualcard *virtualcard, + struct snd_info_buffer *buffer) +{ + static int rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000, + }; + int i; + + if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS) + snd_iprintf(buffer, " continuous"); + if (virtualcard->pcm_hw.rates & SNDRV_PCM_RATE_KNOT) + snd_iprintf(buffer, " knot"); + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (virtualcard->pcm_hw.rates & (1 << i)) + snd_iprintf(buffer, " %d", rates[i]); +} + +#define get_virtualcard_int_ptr(virtualcard, ofs) \ + (unsigned int *)((char *)&((virtualcard)->pcm_hw) + (ofs)) +#define get_virtualcard_ll_ptr(virtualcard, ofs) \ + (unsigned long long *)((char *)&((virtualcard)->pcm_hw) + (ofs)) + +struct virtualcard_hw_field { + const char *name; + const char *format; + unsigned int offset; + unsigned int size; +}; + +#define FIELD_ENTRY(item, fmt) { \ + .name = #item, \ + .format = fmt, \ + .offset = offsetof(struct snd_pcm_hardware, item), \ + .size = sizeof(virtualcard_pcm_hardware.item) \ +} + +static struct virtualcard_hw_field fields[] = { + FIELD_ENTRY(formats, "%#llx"), + FIELD_ENTRY(rates, "%#x"), + FIELD_ENTRY(rate_min, "%d"), + FIELD_ENTRY(rate_max, "%d"), + FIELD_ENTRY(channels_min, "%d"), + FIELD_ENTRY(channels_max, "%d"), + FIELD_ENTRY(buffer_bytes_max, "%ld"), + FIELD_ENTRY(period_bytes_min, "%ld"), + FIELD_ENTRY(period_bytes_max, "%ld"), + FIELD_ENTRY(periods_min, "%d"), + FIELD_ENTRY(periods_max, "%d"), +}; + +static void virtualcard_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_virtualcard *virtualcard = entry->private_data; + int i; + + for (i = 0; i < ARRAY_SIZE(fields); i++) { + snd_iprintf(buffer, "%s ", fields[i].name); + if (fields[i].size == sizeof(int)) + snd_iprintf(buffer, fields[i].format, + *get_virtualcard_int_ptr(virtualcard, + fields[i].offset)); + else + snd_iprintf(buffer, fields[i].format, + *get_virtualcard_ll_ptr(virtualcard, + fields[i].offset)); + if (!strcmp(fields[i].name, "formats")) + print_formats(virtualcard, buffer); + else if (!strcmp(fields[i].name, "rates")) + print_rates(virtualcard, buffer); + snd_iprintf(buffer, "\n"); + } +} + +static void virtualcard_proc_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_virtualcard *virtualcard = entry->private_data; + char line[64]; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + char item[20]; + const char *ptr; + unsigned long long val; + unsigned int offset; + int i; + + ptr = snd_info_get_str(item, line, sizeof(item)); + for (i = 0; i < ARRAY_SIZE(fields); i++) { + if (!strcmp(item, fields[i].name)) + break; + } + if (i >= ARRAY_SIZE(fields)) + continue; + snd_info_get_str(item, ptr, sizeof(item)); + if (kstrtoull(item, 0, &val)) + continue; + + offset = fields[i].offset; + if (fields[i].size == sizeof(int)) + *get_virtualcard_int_ptr(virtualcard, offset) = val; + else + *get_virtualcard_ll_ptr(virtualcard, offset) = val; + } +} + +static void virtualcard_proc_init(struct snd_virtualcard *chip) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(chip->card, "virtualcard_pcm", &entry)) { + snd_info_set_text_ops(entry, chip, virtualcard_proc_read); + entry->c.text.write = virtualcard_proc_write; + entry->mode |= S_IWUSR; + entry->private_data = chip; + } +} +#else +#define virtualcard_proc_init(x) +#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */ + +static int snd_virtualcard_probe(struct platform_device *devptr) +{ + struct snd_card *card; + struct snd_virtualcard *virtualcard; + struct virtualcard_model *m = NULL; + int err; + int dev = devptr->id; + struct snd_pcm_hardware *pcm_hw; + + err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_virtualcard), &card); + if (err < 0) + return err; + virtualcard = card->private_data; + virtualcard->card = card; + virtualcard->fr_info = + (*((struct sndfront_info **)devptr->dev.platform_data)); + + m = virtualcard_models[0]; + + err = snd_card_virtualcard_pcm(virtualcard, 0, 1); + if (err < 0) + goto __nodev; + + virtualcard->pcm_hw = virtualcard_pcm_hardware; + pcm_hw = &virtualcard->pcm_hw; + if (m) { + if (m->formats) + pcm_hw->formats = m->formats; + if (m->buffer_bytes_max) + pcm_hw->buffer_bytes_max = m->buffer_bytes_max; + if (m->period_bytes_min) + pcm_hw->period_bytes_min = m->period_bytes_min; + if (m->period_bytes_max) + pcm_hw->period_bytes_max = m->period_bytes_max; + if (m->periods_min) + pcm_hw->periods_min = m->periods_min; + if (m->periods_max) + pcm_hw->periods_max = m->periods_max; + if (m->rates) + pcm_hw->rates = m->rates; + if (m->rate_min) + pcm_hw->rate_min = m->rate_min; + if (m->rate_max) + pcm_hw->rate_max = m->rate_max; + if (m->channels_min) + pcm_hw->channels_min = m->channels_min; + if (m->channels_max) + pcm_hw->channels_max = m->channels_max; + } + + err = snd_card_virtualcard_new_mixer(virtualcard); + if (err < 0) + goto __nodev; + strcpy(card->driver, "Virtual card"); + strcpy(card->shortname, "Virtual card"); + sprintf(card->longname, "Virtual card %i", dev + 1); + + virtualcard_proc_init(virtualcard); + + snd_card_set_dev(card, &devptr->dev); + + err = snd_card_register(card); + if (err == 0) { + platform_set_drvdata(devptr, card); + return 0; + } +__nodev: + snd_card_free(card); + return err; +} + +static int snd_virtualcard_remove(struct platform_device *devptr) +{ + snd_card_free(platform_get_drvdata(devptr)); + platform_set_drvdata(devptr, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int snd_virtualcard_suspend(struct device *pdev) +{ + struct snd_card *card = dev_get_drvdata(pdev); + struct snd_virtualcard *virtualcard = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(virtualcard->pcm); + return 0; +} + +static int snd_virtualcard_resume(struct device *pdev) +{ + struct snd_card *card = dev_get_drvdata(pdev); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static SIMPLE_DEV_PM_OPS(snd_virtualcard_pm, snd_virtualcard_suspend, + snd_virtualcard_resume); +#define SND_VIRTUALCARD_PM_OPS (&snd_virtualcard_pm) +#else +#define SND_VIRTUALCARD_PM_OPS NULL +#endif + +#define SND_VIRTUALCARD_DRIVER "snd_virtualcard" + +static struct platform_driver snd_virtualcard_driver = { + .probe = snd_virtualcard_probe, + .remove = snd_virtualcard_remove, + .driver = { + .name = SND_VIRTUALCARD_DRIVER, + .owner = THIS_MODULE, + .pm = SND_VIRTUALCARD_PM_OPS, + }, +}; + /** * Entry point to this code when a new device is created. Allocate the basic * structures and the ring buffer for communication with the backend, and @@ -896,22 +1278,29 @@ static int sndfront_probe(struct xenbus_device *dev, } mutex_init(&info->mutex); - mutex_init(&info->tmp_fops_mutex); spin_lock_init(&info->io_lock); init_completion(&info->completion); info->xbdev = dev; - atomic_set(&info->file_refcnt, 0); dev_set_drvdata(&dev->dev, info); err = talk_to_sndback(dev, info); - if (err) { - kfree(info); - dev_set_drvdata(&dev->dev, NULL); - return err; + if (err) + goto err_free_info; + + info->card_dev = platform_device_register_data(NULL, + SND_VIRTUALCARD_DRIVER, + -1, &info, sizeof(info)); + if (IS_ERR(info->card_dev)) { + err = -ENODEV; + goto err_free_info; } - return 0; + +err_free_info: + kfree(info); + dev_set_drvdata(&dev->dev, NULL); + return err; } /** @@ -960,9 +1349,6 @@ sndfront_closing(struct sndfront_info *info) */ static void sndfront_connect(struct sndfront_info *info) { - unsigned int dev_type; - unsigned int dev_num; - unsigned int card_num; unsigned int dev_id; int err; @@ -982,31 +1368,18 @@ static void sndfront_connect(struct sndfront_info *info) xenbus_switch_state(info->xbdev, XenbusStateConnected); - spin_lock_irq(&info->io_lock); - info->connected = SNDIF_STATE_CONNECTED; - spin_unlock_irq(&info->io_lock); - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_type", "%u", - &dev_type, NULL); - if (err) - return; - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_num", "%u", - &dev_num, NULL); - if (err) - return; - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "card_num", "%u", - &card_num, NULL); + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u", + &dev_id, NULL); if (err) return; - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u", - &dev_id, NULL); + err = sndif_add_virt_devices(info, dev_id); if (err) return; - sndif_add_virt_devices(info, dev_type, dev_num, card_num, dev_id); + spin_lock_irq(&info->io_lock); + info->connected = SNDIF_STATE_CONNECTED; + spin_unlock_irq(&info->io_lock); } /** @@ -1046,6 +1419,8 @@ static int sndfront_remove(struct xenbus_device *xbdev) dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename); + platform_device_unregister(info->card_dev); + sndif_free(info, 0); return 0; @@ -1077,36 +1452,19 @@ static int __init xen_snd_front_init(void) if (xen_initial_domain()) return -ENODEV; - ret = register_chrdev(VSND_MAJOR, "vsnd", &vsndcore_fops); - if (ret < 0) - return ret; - - vsndclass = class_create(THIS_MODULE, "vsnd"); - if (!vsndclass) { - ret = -ENOMEM; - goto err_reg_chrdev; - } - - vsndclass->devnode = vsound_devnode; - ret = xenbus_register_frontend(&xen_snd_driver); - if (ret < 0) - goto err_class_destroy; + if (ret) + return ret; - return ret; + ret = platform_driver_register(&snd_virtualcard_driver); -err_class_destroy: - class_destroy(vsndclass); -err_reg_chrdev: - unregister_chrdev(VSND_MAJOR, "vsnd"); return ret; } static void __exit xen_snd_front_cleanup(void) { - class_destroy(vsndclass); - unregister_chrdev(VSND_MAJOR, "vsnd"); xenbus_unregister_driver(&xen_snd_driver); + platform_driver_unregister(&snd_virtualcard_driver); } module_init(xen_snd_front_init); -- 1.9.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |