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

[Xen-devel] [PATCH v3 2/2] xen-sndfront: add capture support



From: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>

Now both play and capture is supported.

Signed-off-by: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx>
---
 sound/drivers/xen-sndfront.c | 193 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 164 insertions(+), 29 deletions(-)

diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c
index d0367c2..054e535 100644
--- a/sound/drivers/xen-sndfront.c
+++ b/sound/drivers/xen-sndfront.c
@@ -98,7 +98,6 @@ MODULE_PARM_DESC(model, "Soundcard model.");
 #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;
@@ -123,7 +122,13 @@ struct sndfront_info {
        unsigned int evtchn, irq;
        struct vsnd_card *vcard;
        int bret_code;
+};
+
+struct vsnd_sndfront {
+       int count;
+       spinlock_t lock; /* protect 'count' member */
        struct platform_device *card_dev;
+       struct sndfront_info *infos[2];
 };
 
 #define GRANT_INVALID_REF      0
@@ -160,7 +165,6 @@ struct snd_virtualcard {
        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];
 };
 
@@ -206,6 +210,8 @@ static struct snd_pcm_hardware virtualcard_pcm_hardware = {
        .fifo_size =            0,
 };
 
+static struct vsnd_sndfront snd_fronts;
+
 static inline
 struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard,
                                     struct snd_pcm_substream *substream)
@@ -216,6 +222,22 @@ struct stream_info *get_vcard_stream(struct 
snd_virtualcard *virtualcard,
                return &virtualcard->streams[1];
 }
 
+static inline
+struct sndfront_info *get_sndfront_info(struct snd_pcm_substream *substream)
+{
+       struct sndfront_info *res = NULL;
+       int stream = substream->stream;
+
+       spin_lock_irq(&snd_fronts.lock);
+       if ((stream == SNDRV_PCM_STREAM_PLAYBACK) && (snd_fronts.count > 0))
+               res = snd_fronts.infos[0];
+       else if ((stream == SNDRV_PCM_STREAM_CAPTURE) && (snd_fronts.count > 1))
+               res =  snd_fronts.infos[1];
+       spin_unlock_irq(&snd_fronts.lock);
+
+       return res;
+}
+
 static unsigned long vmalloc_to_mfn(void *address)
 {
        return pfn_to_mfn(vmalloc_to_pfn(address));
@@ -234,7 +256,8 @@ static inline void flush_requests(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)
+                                   unsigned int rate,
+                                   unsigned int stream)
 {
        struct sndif_request *req;
 
@@ -248,6 +271,7 @@ static int sndif_queue_request_open(struct sndfront_info 
*info,
        req->u.open.snd_params.format = format;
        req->u.open.snd_params.channels = channels;
        req->u.open.snd_params.rate = rate;
+       req->u.open.stream = stream;
        info->ring.req_prod_pvt++;
 
        flush_requests(info);
@@ -300,16 +324,48 @@ static int sndif_queue_request_write(struct sndfront_info 
*info,
        return 0;
 }
 
+static int sndif_queue_request_read(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_READ;
+       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];
+
+       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 int rate,
+                        unsigned int stream)
 {
        unsigned long answer_tout;
 
+       if (!info)
+               return -EFAULT;
+
        reinit_completion(&info->completion);
 
-       if (sndif_queue_request_open(info, format, channels, rate))
+       if (sndif_queue_request_open(info, format, channels, rate, stream))
                return -EIO;
 
        answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT);
@@ -324,6 +380,9 @@ static int alsa_pcm_close(struct sndfront_info *info)
 {
        unsigned long answer_tout;
 
+       if (!info)
+               return -EFAULT;
+
        reinit_completion(&info->completion);
 
        if (sndif_queue_request_close(info))
@@ -343,6 +402,9 @@ static int alsa_pcm_write(struct sndfront_info *info, char 
__user *buf,
        unsigned char *shared_data;
        unsigned long answer_tout;
 
+       if (!info)
+               return -EFAULT;
+
        shared_data = info->vcard->buf;
 
        if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
@@ -364,6 +426,35 @@ static int alsa_pcm_write(struct sndfront_info *info, char 
__user *buf,
        return info->bret_code;
 }
 
+static int alsa_pcm_read(struct sndfront_info *info, char __user *buf,
+                        int len)
+{
+       unsigned char *shared_data;
+       unsigned long answer_tout;
+
+       if (!info)
+               return -EFAULT;
+
+       shared_data = info->vcard->buf;
+
+       if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST)
+               return -EFAULT;
+
+       reinit_completion(&info->completion);
+
+       if (sndif_queue_request_read(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;
+
+       if (copy_to_user(buf, shared_data, len))
+               return -EFAULT;
+
+       return info->bret_code;
+}
 static int alsa_pcm_silence(struct sndfront_info *info, int len)
 {
        unsigned char *shared_data;
@@ -458,7 +549,6 @@ static int sndif_add_virt_devices(struct sndfront_info 
*info,
                return -ENOMEM;
 
        vcard->stream_id = stream_id;
-       vcard->fr_info = info;
 
        info->vcard = vcard;
 
@@ -520,6 +610,7 @@ static irqreturn_t sndif_interrupt(int irq, void *data)
                case SNDIF_OP_OPEN:
                case SNDIF_OP_CLOSE:
                case SNDIF_OP_WRITE:
+               case SNDIF_OP_READ:
                        if (unlikely(bret->status != SNDIF_RSP_OKAY))
                                dev_dbg(&info->xbdev->dev,
                                        "snddev data request error: %x\n",
@@ -677,16 +768,19 @@ static int virtualcard_pcm_prepare(struct 
snd_pcm_substream *substream)
        if ((runtime->rate != vcard_stream->rate) ||
            (runtime->channels != vcard_stream->channels) ||
            (runtime->format != vcard_stream->format)) {
+               struct sndfront_info *info = get_sndfront_info(substream);
+
                if (vcard_stream->opened) {
-                       err = alsa_pcm_close(virtualcard->fr_info);
+                       err = alsa_pcm_close(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);
+
+               err = alsa_pcm_open(info, runtime->format, runtime->channels,
+                                   runtime->rate, substream->stream);
                if (err)
                        return err;
 
@@ -699,8 +793,8 @@ static int virtualcard_pcm_prepare(struct snd_pcm_substream 
*substream)
        return 0;
 }
 
-static
-snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+virtualcard_pcm_playback_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;
@@ -721,6 +815,23 @@ snd_pcm_uframes_t virtualcard_pcm_pointer(struct 
snd_pcm_substream *substream)
        return vcard_stream->position;
 }
 
+static snd_pcm_uframes_t
+virtualcard_pcm_capture_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, res;
+       struct stream_info *vcard_stream;
+
+       vcard_stream = get_vcard_stream(virtualcard, substream);
+
+#ifdef MAX_CAPTURE_SIZE
+       res = (vcard_stream->position + MAX_CAPTURE_SIZE) % buff_size;
+#else
+       res = (vcard_stream->position + buff_size / 2) % buff_size;
+#endif
+       return res;
+}
+
 static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream,
                                     struct snd_pcm_hw_params *hw_params)
 {
@@ -763,8 +874,9 @@ static int virtualcard_pcm_close(struct snd_pcm_substream 
*substream)
 {
        int err;
        struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream);
+       struct sndfront_info *info = get_sndfront_info(substream);
 
-       err = alsa_pcm_close(virtualcard->fr_info);
+       err = alsa_pcm_close(info);
        if (err)
                return err;
 
@@ -782,11 +894,11 @@ static int virtualcard_pcm_playback_copy(struct 
snd_pcm_substream *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;
+       struct sndfront_info *info = get_sndfront_info(substream);
 
        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));
+       return alsa_pcm_write(info, src, frames_to_bytes(runtime, count));
 }
 
 static int virtualcard_pcm_playback_silence(struct snd_pcm_substream 
*substream,
@@ -797,12 +909,12 @@ static int virtualcard_pcm_playback_silence(struct 
snd_pcm_substream *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;
+       struct sndfront_info *info = get_sndfront_info(substream);
 
        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));
+       return alsa_pcm_silence(info, frames_to_bytes(runtime, count));
 }
 
 static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream,
@@ -814,11 +926,12 @@ static int virtualcard_pcm_capture_copy(struct 
snd_pcm_substream *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;
+       struct sndfront_info *info = get_sndfront_info(substream);
 
        vcard_stream->position = (stream_pos + count) % runtime->buffer_size;
        vcard_stream->crossed = count / runtime->buffer_size;
 
-       return 0;
+       return alsa_pcm_read(info, dst, frames_to_bytes(runtime, count));
 }
 
 static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
@@ -829,7 +942,7 @@ static struct snd_pcm_ops virtualcard_pcm_playback_ops = {
        .hw_free =      virtualcard_pcm_hw_free,
        .prepare =      virtualcard_pcm_prepare,
        .trigger =      virtualcard_pcm_trigger,
-       .pointer =      virtualcard_pcm_pointer,
+       .pointer =      virtualcard_pcm_playback_pointer,
        .copy =         virtualcard_pcm_playback_copy,
        .silence =      virtualcard_pcm_playback_silence,
 };
@@ -842,7 +955,7 @@ static struct snd_pcm_ops virtualcard_pcm_capture_ops = {
        .hw_free =      virtualcard_pcm_hw_free,
        .prepare =      virtualcard_pcm_prepare,
        .trigger =      virtualcard_pcm_trigger,
-       .pointer =      virtualcard_pcm_pointer,
+       .pointer =      virtualcard_pcm_capture_pointer,
        .copy =         virtualcard_pcm_capture_copy,
 };
 
@@ -1160,8 +1273,6 @@ static int snd_virtualcard_probe(struct platform_device 
*devptr)
                return err;
        virtualcard = card->private_data;
        virtualcard->card = card;
-       virtualcard->fr_info =
-               (*((struct sndfront_info **)devptr->dev.platform_data));
 
        m = virtualcard_models[0];
 
@@ -1271,7 +1382,7 @@ static struct platform_driver snd_virtualcard_driver = {
 static int sndfront_probe(struct xenbus_device *dev,
                          const struct xenbus_device_id *id)
 {
-       int err;
+       int err, count;
        struct sndfront_info *info;
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -1291,12 +1402,26 @@ static int sndfront_probe(struct xenbus_device *dev,
        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;
+       spin_lock_irq(&snd_fronts.lock);
+       snd_fronts.infos[snd_fronts.count] = info;
+       snd_fronts.count++;
+       count = snd_fronts.count;
+       spin_unlock_irq(&snd_fronts.lock);
+
+       if (count == 1) {
+               struct platform_device *card_dev;
+
+               card_dev = platform_device_register_data(NULL,
+                                                        SND_VIRTUALCARD_DRIVER,
+                                                        -1, NULL, 0);
+               snd_fronts.card_dev = card_dev;
+               if (IS_ERR(snd_fronts.card_dev)) {
+                       spin_lock_irq(&snd_fronts.lock);
+                       snd_fronts.count = 0;
+                       spin_unlock_irq(&snd_fronts.lock);
+                       err = -ENODEV;
+                       goto err_free_info;
+               }
        }
        return 0;
 
@@ -1418,11 +1543,19 @@ static void sndback_changed(struct xenbus_device *dev,
 
 static int sndfront_remove(struct xenbus_device *xbdev)
 {
+       int count;
        struct sndfront_info *info = dev_get_drvdata(&xbdev->dev);
 
        dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
 
-       platform_device_unregister(info->card_dev);
+       spin_lock_irq(&snd_fronts.lock);
+       snd_fronts.count--;
+       count = snd_fronts.count;
+       snd_fronts.infos[count] = NULL;
+       spin_lock_irq(&snd_fronts.lock);
+
+       if (!count)
+               platform_device_unregister(snd_fronts.card_dev);
 
        sndif_free(info, 0);
 
@@ -1447,6 +1580,8 @@ static int __init xen_snd_front_init(void)
 {
        int ret = 0;
 
+       snd_fronts.count = 0;
+       spin_lock_init(&snd_fronts.lock);
        /*FIXME: xen_pv_domain() should be here, but ARM hardcoded to hvm*/
        if (!xen_domain())
                return -ENODEV;
-- 
1.9.1


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


 


Rackspace

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