diff options
Diffstat (limited to 'sound/virtio/virtio_pcm_msg.c')
-rw-r--r-- | sound/virtio/virtio_pcm_msg.c | 79 |
1 files changed, 46 insertions, 33 deletions
diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c index aca2dc1989..542446c4c7 100644 --- a/sound/virtio/virtio_pcm_msg.c +++ b/sound/virtio/virtio_pcm_msg.c @@ -155,7 +155,6 @@ int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss, sizeof(msg->xfer)); sg_init_one(&msg->sgs[PCM_MSG_SG_STATUS], &msg->status, sizeof(msg->status)); - msg->length = period_bytes; virtsnd_pcm_sg_from(&msg->sgs[PCM_MSG_SG_DATA], sg_num, data, period_bytes); @@ -186,61 +185,75 @@ void virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss) /** * virtsnd_pcm_msg_send() - Send asynchronous I/O messages. * @vss: VirtIO PCM substream. + * @offset: starting position that has been updated + * @bytes: number of bytes that has been updated * * All messages are organized in an ordered circular list. Each time the * function is called, all currently non-enqueued messages are added to the - * virtqueue. For this, the function keeps track of two values: - * - * msg_last_enqueued = index of the last enqueued message, - * msg_count = # of pending messages in the virtqueue. + * virtqueue. For this, the function uses offset and bytes to calculate the + * messages that need to be added. * * Context: Any context. Expects the tx/rx queue and the VirtIO substream * spinlocks to be held by caller. * Return: 0 on success, -errno on failure. */ -int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss) +int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss, unsigned long offset, + unsigned long bytes) { - struct snd_pcm_runtime *runtime = vss->substream->runtime; struct virtio_snd *snd = vss->snd; struct virtio_device *vdev = snd->vdev; struct virtqueue *vqueue = virtsnd_pcm_queue(vss)->vqueue; - int i; - int n; + unsigned long period_bytes = snd_pcm_lib_period_bytes(vss->substream); + unsigned long start, end, i; + unsigned int msg_count = vss->msg_count; bool notify = false; + int rc; - i = (vss->msg_last_enqueued + 1) % runtime->periods; - n = runtime->periods - vss->msg_count; + start = offset / period_bytes; + end = (offset + bytes - 1) / period_bytes; - for (; n; --n, i = (i + 1) % runtime->periods) { + for (i = start; i <= end; i++) { struct virtio_pcm_msg *msg = vss->msgs[i]; struct scatterlist *psgs[] = { &msg->sgs[PCM_MSG_SG_XFER], &msg->sgs[PCM_MSG_SG_DATA], &msg->sgs[PCM_MSG_SG_STATUS] }; - int rc; - - msg->xfer.stream_id = cpu_to_le32(vss->sid); - memset(&msg->status, 0, sizeof(msg->status)); - - if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK) - rc = virtqueue_add_sgs(vqueue, psgs, 2, 1, msg, - GFP_ATOMIC); - else - rc = virtqueue_add_sgs(vqueue, psgs, 1, 2, msg, - GFP_ATOMIC); - - if (rc) { - dev_err(&vdev->dev, - "SID %u: failed to send I/O message\n", - vss->sid); - return rc; + unsigned long n; + + n = period_bytes - (offset % period_bytes); + if (n > bytes) + n = bytes; + + msg->length += n; + if (msg->length == period_bytes) { + msg->xfer.stream_id = cpu_to_le32(vss->sid); + memset(&msg->status, 0, sizeof(msg->status)); + + if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK) + rc = virtqueue_add_sgs(vqueue, psgs, 2, 1, msg, + GFP_ATOMIC); + else + rc = virtqueue_add_sgs(vqueue, psgs, 1, 2, msg, + GFP_ATOMIC); + + if (rc) { + dev_err(&vdev->dev, + "SID %u: failed to send I/O message\n", + vss->sid); + return rc; + } + + vss->msg_count++; } - vss->msg_last_enqueued = i; - vss->msg_count++; + offset = 0; + bytes -= n; } + if (msg_count == vss->msg_count) + return 0; + if (!(vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING))) notify = virtqueue_kick_prepare(vqueue); @@ -309,6 +322,8 @@ static void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg, if (vss->hw_ptr >= vss->buffer_bytes) vss->hw_ptr -= vss->buffer_bytes; + msg->length = 0; + vss->xfer_xrun = false; vss->msg_count--; @@ -320,8 +335,6 @@ static void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg, le32_to_cpu(msg->status.latency_bytes)); schedule_work(&vss->elapsed_period); - - virtsnd_pcm_msg_send(vss); } else if (!vss->msg_count) { wake_up_all(&vss->msg_empty); } |