Commit f40a2867 authored by Anton Yakovlev's avatar Anton Yakovlev Committed by Takashi Iwai
Browse files

ALSA: virtio: handling control and I/O messages for the PCM device



The driver implements a message-based transport for I/O substream
operations. Before the start of the substream, the hardware buffer is
sliced into I/O messages, the number of which is equal to the current
number of periods. The size of each message is equal to the current
size of one period.

I/O messages are organized in an ordered queue. The completion of the
I/O message indicates an elapsed period (the only exception is the end
of the stream for the capture substream). Upon completion, the message
is automatically re-added to the end of the queue.

Signed-off-by: default avatarAnton Yakovlev <anton.yakovlev@opensynergy.com>
Link: https://lore.kernel.org/r/20210302164709.3142702-6-anton.yakovlev@opensynergy.com


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 29b96bf5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -5,5 +5,6 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
virtio_snd-objs := \
	virtio_card.o \
	virtio_ctl_msg.o \
	virtio_pcm.o
	virtio_pcm.o \
	virtio_pcm_msg.o
+18 −4
Original line number Diff line number Diff line
@@ -55,6 +55,12 @@ static void virtsnd_event_send(struct virtqueue *vqueue,
static void virtsnd_event_dispatch(struct virtio_snd *snd,
				   struct virtio_snd_event *event)
{
	switch (le32_to_cpu(event->hdr.code)) {
	case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
	case VIRTIO_SND_EVT_PCM_XRUN:
		virtsnd_pcm_event(snd, event);
		break;
	}
}

/**
@@ -101,11 +107,15 @@ static int virtsnd_find_vqs(struct virtio_snd *snd)
	struct virtio_device *vdev = snd->vdev;
	static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
		[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
		[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb
		[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
		[VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
		[VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
	};
	static const char *names[VIRTIO_SND_VQ_MAX] = {
		[VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
		[VIRTIO_SND_VQ_EVENT] = "virtsnd-event"
		[VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
		[VIRTIO_SND_VQ_TX] = "virtsnd-tx",
		[VIRTIO_SND_VQ_RX] = "virtsnd-rx"
	};
	struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
	unsigned int i;
@@ -318,8 +328,12 @@ static void virtsnd_remove(struct virtio_device *vdev)
	vdev->config->del_vqs(vdev);
	vdev->config->reset(vdev);

	for (i = 0; snd->substreams && i < snd->nsubstreams; ++i)
		cancel_work_sync(&snd->substreams[i].elapsed_period);
	for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
		struct virtio_pcm_substream *vss = &snd->substreams[i];

		cancel_work_sync(&vss->elapsed_period);
		virtsnd_pcm_msg_free(vss);
	}

	kfree(snd->event_msgs);
}
+9 −0
Original line number Diff line number Diff line
@@ -79,4 +79,13 @@ virtsnd_rx_queue(struct virtio_snd *snd)
	return &snd->queues[VIRTIO_SND_VQ_RX];
}

static inline struct virtio_snd_queue *
virtsnd_pcm_queue(struct virtio_pcm_substream *vss)
{
	if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK)
		return virtsnd_tx_queue(vss->snd);
	else
		return virtsnd_rx_queue(vss->snd);
}

#endif /* VIRTIO_SND_CARD_H */
+32 −0
Original line number Diff line number Diff line
@@ -353,6 +353,8 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd)
		vss->snd = snd;
		vss->sid = i;
		INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed);
		init_waitqueue_head(&vss->msg_empty);
		spin_lock_init(&vss->lock);

		rc = virtsnd_pcm_build_hw(vss, &info[i]);
		if (rc)
@@ -477,3 +479,33 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd)

	return 0;
}

/**
 * virtsnd_pcm_event() - Handle the PCM device event notification.
 * @snd: VirtIO sound device.
 * @event: VirtIO sound event.
 *
 * Context: Interrupt context.
 */
void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event)
{
	struct virtio_pcm_substream *vss;
	u32 sid = le32_to_cpu(event->data);

	if (sid >= snd->nsubstreams)
		return;

	vss = &snd->substreams[sid];

	switch (le32_to_cpu(event->hdr.code)) {
	case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
		/* TODO: deal with shmem elapsed period */
		break;
	case VIRTIO_SND_EVT_PCM_XRUN:
		spin_lock(&vss->lock);
		if (vss->xfer_enabled)
			vss->xfer_xrun = true;
		spin_unlock(&vss->lock);
		break;
	}
}
+40 −0
Original line number Diff line number Diff line
@@ -23,6 +23,17 @@ struct virtio_pcm_msg;
 * @substream: Kernel ALSA substream.
 * @hw: Kernel ALSA substream hardware descriptor.
 * @elapsed_period: Kernel work to handle the elapsed period state.
 * @lock: Spinlock that protects fields shared by interrupt handlers and
 *        substream operators.
 * @buffer_bytes: Current buffer size in bytes.
 * @hw_ptr: Substream hardware pointer value in bytes [0 ... buffer_bytes).
 * @xfer_enabled: Data transfer state (0 - off, 1 - on).
 * @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun).
 * @msgs: Allocated I/O messages.
 * @nmsgs: Number of allocated I/O messages.
 * @msg_last_enqueued: Index of the last I/O message added to the virtqueue.
 * @msg_count: Number of pending I/O messages in the virtqueue.
 * @msg_empty: Notify when msg_count is zero.
 */
struct virtio_pcm_substream {
	struct virtio_snd *snd;
@@ -33,6 +44,16 @@ struct virtio_pcm_substream {
	struct snd_pcm_substream *substream;
	struct snd_pcm_hardware hw;
	struct work_struct elapsed_period;
	spinlock_t lock;
	size_t buffer_bytes;
	size_t hw_ptr;
	bool xfer_enabled;
	bool xfer_xrun;
	struct virtio_pcm_msg **msgs;
	unsigned int nmsgs;
	int msg_last_enqueued;
	unsigned int msg_count;
	wait_queue_head_t msg_empty;
};

/**
@@ -65,8 +86,27 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd);

int virtsnd_pcm_build_devs(struct virtio_snd *snd);

void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event);

void virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue);

void virtsnd_pcm_rx_notify_cb(struct virtqueue *vqueue);

struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid);

struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid);

struct virtio_snd_msg *
virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *vss,
			  unsigned int command, gfp_t gfp);

int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss,
			  unsigned int periods, unsigned int period_bytes);

void virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss);

int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss);

unsigned int virtsnd_pcm_msg_pending_num(struct virtio_pcm_substream *vss);

#endif /* VIRTIO_SND_PCM_H */
Loading