You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/08/11 12:40:47 UTC

[nuttx] branch master updated: audio:add virtio snd driver

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 3cabf7ced8 audio:add virtio snd driver
3cabf7ced8 is described below

commit 3cabf7ced82d2e8bc36877808aa8bc0ee998545a
Author: shipei <sh...@xiaomi.com>
AuthorDate: Mon Jun 26 21:34:19 2023 +0800

    audio:add virtio snd driver
    
    playback test:
    nxplayer
    device /dev/audio/pcm0p
    playraw /data/test.wav 2 16 44100
    stop
    
    record test:
    nxrecorder
    device /dev/audio/pcm0c
    recordraw /data/rec.pcm 2 16 16000
    stop
    
    Signed-off-by: shipei <sh...@xiaomi.com>
---
 drivers/virtio/CMakeLists.txt |    4 +
 drivers/virtio/Kconfig        |   16 +
 drivers/virtio/Make.defs      |    3 +
 drivers/virtio/virtio-snd.c   | 1188 +++++++++++++++++++++++++++++++++++++++++
 drivers/virtio/virtio-snd.h   |  362 +++++++++++++
 drivers/virtio/virtio.c       |   11 +-
 6 files changed, 1583 insertions(+), 1 deletion(-)

diff --git a/drivers/virtio/CMakeLists.txt b/drivers/virtio/CMakeLists.txt
index ebd9ea25c5..9d9097e7ea 100644
--- a/drivers/virtio/CMakeLists.txt
+++ b/drivers/virtio/CMakeLists.txt
@@ -43,5 +43,9 @@ if(CONFIG_DRIVERS_VIRTIO_SERIAL)
   list(APPEND SRCS virtio-serial.c)
 endif()
 
+if(CONFIG_DRIVERS_VIRTIO_SOUND)
+  list(APPEND SRCS virtio-snd.c)
+endif()
+
 target_sources(drivers PRIVATE ${SRCS})
 target_include_directories(drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 76c24d90d6..fd74fee369 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -66,4 +66,20 @@ config DRIVERS_VIRTIO_SERIAL_BUFSIZE
 	int "Virtio serial driver buffer size"
 	default 256
 endif
+
+config DRIVERS_VIRTIO_SOUND
+	bool "Virtio sound support"
+	default n
+	depends on DRIVERS_AUDIO
+
+if DRIVERS_VIRTIO_SOUND
+config DRIVERS_VIRTIO_SOUND_PERIOD_TIME
+	int "Virtio snd driver period time"
+	default 40
+
+config DRIVERS_VIRTIO_SND_BUFFER_COUNT
+	int "Virtio snd driver buffer count"
+	default 6
+endif
+
 endif # DRIVERS_VIRTIO
diff --git a/drivers/virtio/Make.defs b/drivers/virtio/Make.defs
index 20df7ce537..fd257163ba 100644
--- a/drivers/virtio/Make.defs
+++ b/drivers/virtio/Make.defs
@@ -48,6 +48,9 @@ ifeq ($(CONFIG_DRIVERS_VIRTIO_SERIAL),y)
   CSRCS += virtio-serial.c
 endif
 
+ifeq ($(CONFIG_DRIVERS_VIRTIO_SOUND),y)
+  CSRCS += virtio-snd.c
+endif
 # Include build support
 
 DEPPATH += --dep-path virtio
diff --git a/drivers/virtio/virtio-snd.c b/drivers/virtio/virtio-snd.c
new file mode 100644
index 0000000000..22d3b2c870
--- /dev/null
+++ b/drivers/virtio/virtio-snd.c
@@ -0,0 +1,1188 @@
+/****************************************************************************
+ * drivers/virtio/virtio-snd.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/param.h>
+
+#include <nuttx/audio/audio.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/virtio/virtio.h>
+#include <nuttx/semaphore.h>
+
+#include "virtio-snd.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Map for converting VirtIO frame rate to nuttx frame rate */
+
+struct virtio_snd_rate_map_s
+{
+  unsigned int nrate;
+  unsigned int sps;
+};
+
+/* Map for nuttx bps to virtio pcm subformat */
+
+struct virtio_snd_format_map_s
+{
+  uint8_t nformat;
+  unsigned int bps;
+};
+
+/* Buffer for pcm data tx/rx transfer */
+
+struct virtio_snd_buffer_s
+{
+  struct ap_buffer_s apb;
+  struct virtio_snd_pcm_xfer xfer;
+  struct virtio_snd_pcm_status status;
+  FAR struct audio_lowerhalf_s *dev;
+};
+
+/* Include struct audio_lowerhalf_s, use to get struct virtio_snd_s */
+
+struct virtio_snd_dev_s
+{
+  struct audio_lowerhalf_s dev;
+  uint32_t period_bytes;
+  uint32_t index;
+  FAR void *priv;
+};
+
+/* Virtio snd card struct for virtio driver */
+
+struct virtio_snd_s
+{
+  FAR struct virtio_device *vdev;
+  FAR struct virtio_snd_dev_s *dev;
+  FAR struct virtio_snd_pcm_info *info;
+  struct virtio_snd_config config;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void virtio_snd_pcm_notify_cb(FAR struct virtqueue *vqueue);
+static void virtio_snd_ctl_notify_cb(FAR struct virtqueue *vqueue);
+static void virtio_snd_event_notify_cb(FAR struct virtqueue *vqueue);
+
+static unsigned int virtio_snd_get_period_bytes(unsigned int rate,
+                                                unsigned int ch,
+                                                unsigned int bps,
+                                                unsigned int period_time);
+static unsigned int
+virtio_snd_get_support_rates(FAR const struct virtio_snd_pcm_info *info);
+static void
+virtio_snd_get_support_formats(FAR const struct virtio_snd_pcm_info *info,
+                               FAR struct audio_caps_s *caps);
+
+static int virtio_snd_send_pcm(FAR struct virtio_snd_dev_s *sdev,
+                               FAR struct virtio_snd_buffer_s *buf);
+static int virtio_snd_send_ctl(FAR struct virtio_snd_s *priv,
+                               FAR struct virtqueue_buf *vb,
+                               int readable,
+                               int writable);
+static int virtio_snd_query_info(FAR struct virtio_snd_s *priv,
+                                 int cmd,
+                                 size_t size,
+                                 size_t count,
+                                 FAR struct virtio_snd_pcm_info *info);
+static int virtio_snd_set_params(FAR struct virtio_snd_dev_s *sdev,
+                                 unsigned int ch,
+                                 unsigned int rate,
+                                 unsigned int bps);
+static int virtio_snd_send_cmd(FAR struct virtio_snd_dev_s *sdev,
+                               int cmd);
+
+static int virtio_snd_getcaps(FAR struct audio_lowerhalf_s *dev,
+                              int type,
+                              FAR struct audio_caps_s *caps);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_configure(FAR struct audio_lowerhalf_s *dev,
+                                FAR void *session,
+                                FAR const struct audio_caps_s *caps);
+static int virtio_snd_start(FAR struct audio_lowerhalf_s *dev,
+                            FAR void *session);
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+static int virtio_snd_stop(FAR struct audio_lowerhalf_s *dev,
+                           FAR void *session);
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+static int virtio_snd_pause(FAR struct audio_lowerhalf_s *dev,
+                            FAR void *session);
+static int virtio_snd_resume(FAR struct audio_lowerhalf_s *dev,
+                             FAR void *session);
+#endif
+static int virtio_snd_reserve(FAR struct audio_lowerhalf_s *dev,
+                              FAR void **session);
+static int virtio_snd_release(FAR struct audio_lowerhalf_s *dev,
+                              FAR void *session);
+#else
+static int virtio_snd_configure(FAR struct audio_lowerhalf_s *dev,
+                                FAR const struct audio_caps_s *caps);
+static int virtio_snd_start(FAR struct audio_lowerhalf_s *dev);
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+static int virtio_snd_stop(FAR struct audio_lowerhalf_s *dev);
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+static int virtio_snd_pause(FAR struct audio_lowerhalf_s *dev);
+static int virtio_snd_resume(FAR struct audio_lowerhalf_s *dev);
+#endif
+static int virtio_snd_reserve(FAR struct audio_lowerhalf_s *dev);
+static int virtio_snd_release(FAR struct audio_lowerhalf_s *dev);
+#endif
+static int virtio_snd_allocbuffer(FAR struct audio_lowerhalf_s *dev,
+                                  FAR struct audio_buf_desc_s *apb);
+static int virtio_snd_freebuffer(FAR struct audio_lowerhalf_s *dev,
+                                 FAR struct audio_buf_desc_s *apb);
+static int virtio_snd_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
+                                    FAR struct ap_buffer_s *apb);
+static int virtio_snd_ioctl(FAR struct audio_lowerhalf_s *dev,
+                            int cmd,
+                            unsigned long arg);
+static int virtio_snd_shutdown(FAR struct audio_lowerhalf_s *dev);
+
+static int virtio_snd_probe(FAR struct virtio_device *vdev);
+static void virtio_snd_remove(FAR struct virtio_device *vdev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct virtio_snd_rate_map_s g_rate_map[] =
+{
+  [VIRTIO_SND_PCM_RATE_8000] =
+    {
+      AUDIO_SAMP_RATE_8K, 8000
+    },
+  [VIRTIO_SND_PCM_RATE_11025] =
+    {
+      AUDIO_SAMP_RATE_11K, 11025
+    },
+  [VIRTIO_SND_PCM_RATE_16000] =
+    {
+      AUDIO_SAMP_RATE_16K, 16000
+    },
+  [VIRTIO_SND_PCM_RATE_22050] =
+    {
+      AUDIO_SAMP_RATE_22K, 22050
+    },
+  [VIRTIO_SND_PCM_RATE_32000] =
+    {
+      AUDIO_SAMP_RATE_32K, 32000
+    },
+  [VIRTIO_SND_PCM_RATE_44100] =
+    {
+      AUDIO_SAMP_RATE_44K, 44100
+    },
+  [VIRTIO_SND_PCM_RATE_48000] =
+    {
+      AUDIO_SAMP_RATE_48K, 48000
+    },
+  [VIRTIO_SND_PCM_RATE_96000] =
+    {
+      AUDIO_SAMP_RATE_96K, 96000
+    },
+  [VIRTIO_SND_PCM_RATE_192000] =
+    {
+      AUDIO_SAMP_RATE_192K, 192000
+    }
+};
+
+static const struct virtio_snd_format_map_s g_format_map[] =
+{
+  [VIRTIO_SND_PCM_FMT_S8] =
+    {
+      AUDIO_SUBFMT_PCM_S8, 8
+    },
+  [VIRTIO_SND_PCM_FMT_S16] =
+    {
+      AUDIO_SUBFMT_PCM_S16_LE, 16
+    },
+  [VIRTIO_SND_PCM_FMT_S32] =
+    {
+      AUDIO_SUBFMT_PCM_S32_LE, 32
+    }
+};
+
+static struct virtio_driver g_virtio_snd_driver =
+{
+  LIST_INITIAL_VALUE(g_virtio_snd_driver.node), /* node */
+  VIRTIO_ID_SOUND,                              /* device id */
+  virtio_snd_probe,                             /* probe */
+  virtio_snd_remove,                            /* remove */
+};
+
+static const struct audio_ops_s g_virtio_snd_ops =
+{
+  virtio_snd_getcaps,        /* getcaps        */
+  virtio_snd_configure,      /* configure      */
+  virtio_snd_shutdown,       /* shutdown       */
+  virtio_snd_start,          /* start          */
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+  virtio_snd_stop,           /* stop           */
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+  virtio_snd_pause,          /* pause          */
+  virtio_snd_resume,         /* resume         */
+#endif
+  virtio_snd_allocbuffer,    /* allocbuffer    */
+  virtio_snd_freebuffer,     /* freebuffer     */
+  virtio_snd_enqueuebuffer,  /* enqueuebuffer */
+  NULL,                      /* cancelbuffer  */
+  virtio_snd_ioctl,          /* ioctl          */
+  NULL,                      /* read           */
+  NULL,                      /* write          */
+  virtio_snd_reserve,        /* reserve        */
+  virtio_snd_release         /* release        */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtio_snd_get_period_bytes
+ ****************************************************************************/
+
+static unsigned int virtio_snd_get_period_bytes(unsigned int rate,
+                                                unsigned int ch,
+                                                unsigned int bps,
+                                                unsigned int period_time)
+{
+  return rate * ch * (bps / 8) * period_time / 1000;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_get_support_rates
+ ****************************************************************************/
+
+static unsigned int
+virtio_snd_get_support_rates(FAR const struct virtio_snd_pcm_info *info)
+{
+  unsigned int rates = 0;
+  int i;
+
+  for (i = VIRTIO_SND_PCM_RATE_5512; i < VIRTIO_SND_PCM_RATE_384000; i++)
+    {
+      if (info->rates & (1 << i))
+        {
+          rates |= g_rate_map[i].nrate;
+        }
+    }
+
+  return rates;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_get_support_formats
+ ****************************************************************************/
+
+static void
+virtio_snd_get_support_formats(FAR const struct virtio_snd_pcm_info *info,
+                               FAR struct audio_caps_s *caps)
+{
+  size_t subformats = 0;
+  size_t i;
+
+  for (i = 0; i < nitems(g_format_map); i++)
+    {
+      if (info->formats & (1 << i))
+        {
+          caps->ac_controls.b[subformats++] = g_format_map[i].nformat;
+          if (subformats >= nitems(caps->ac_controls.b))
+            break;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: virtio_snd_pcm_notify_cb
+ ****************************************************************************/
+
+static void virtio_snd_pcm_notify_cb(FAR struct virtqueue *vq)
+{
+  for (; ; )
+    {
+      FAR struct virtio_snd_buffer_s *buf;
+      buf = virtqueue_get_buffer(vq, NULL, NULL);
+      if (buf == NULL)
+        {
+          break;
+        }
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+      buf->dev->upper(buf->dev->priv, AUDIO_CALLBACK_DEQUEUE,
+                      &buf->apb, OK, NULL);
+#else
+      buf->dev->upper(buf->dev->priv, AUDIO_CALLBACK_DEQUEUE,
+                      &buf->apb, OK);
+#endif
+    }
+}
+
+/****************************************************************************
+ * Name: virtio_snd_ctl_notify_cb
+ ****************************************************************************/
+
+static void virtio_snd_ctl_notify_cb(FAR struct virtqueue *vq)
+{
+  FAR sem_t *ctl_sem;
+
+  ctl_sem = virtqueue_get_buffer(vq, NULL, NULL);
+  nxsem_post(ctl_sem);
+}
+
+/****************************************************************************
+ * Name: virtio_snd_event_notify_cb
+ ****************************************************************************/
+
+static void virtio_snd_event_notify_cb(FAR struct virtqueue *vqueue)
+{
+  vrtinfo("recvive jack/pcm event\n");
+}
+
+/****************************************************************************
+ * Name: virtio_snd_send_pcm
+ ****************************************************************************/
+
+static int virtio_snd_send_pcm(FAR struct virtio_snd_dev_s *sdev,
+                               FAR struct virtio_snd_buffer_s *buf)
+{
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_snd_pcm_info *info = &priv->info[sdev->index];
+  int idx = info->direction == VIRTIO_SND_D_INPUT ?
+                               VIRTIO_SND_VQ_RX : VIRTIO_SND_VQ_TX;
+  FAR struct virtqueue *vq = priv->vdev->vrings_info[idx].vq;
+  struct virtqueue_buf vb[3];
+
+  vb[0].buf = &buf->xfer;
+  vb[0].len = sizeof(buf->xfer);
+  vb[1].buf = buf->apb.samp;
+  vb[1].len = sdev->period_bytes;
+  vb[2].buf = &buf->status;
+  vb[2].len = sizeof(buf->status);
+
+  if (idx == VIRTIO_SND_VQ_RX)
+    {
+      virtqueue_add_buffer(vq, vb, 1, 2, buf);
+    }
+  else
+    {
+      virtqueue_add_buffer(vq, vb, 2, 1, buf);
+    }
+
+  virtqueue_kick(vq);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_send_ctl
+ ****************************************************************************/
+
+static int virtio_snd_send_ctl(FAR struct virtio_snd_s *priv,
+                               FAR struct virtqueue_buf *vb,
+                               int readable,
+                               int writable)
+{
+  FAR struct virtqueue *vq =
+    priv->vdev->vrings_info[VIRTIO_SND_VQ_CONTROL].vq;
+  sem_t ctl_sem;
+  int ret;
+
+  nxsem_init(&ctl_sem, 0, 0);
+
+  virtqueue_add_buffer(vq, vb, readable, writable, &ctl_sem);
+  virtqueue_kick(vq);
+
+  ret = nxsem_wait_uninterruptible(&ctl_sem);
+  nxsem_destroy(&ctl_sem);
+  if (ret < 0)
+    {
+      vrterr("nxsem wait error:%d\n", ret);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_query_info
+ ****************************************************************************/
+
+static int virtio_snd_query_info(FAR struct virtio_snd_s *priv,
+                                 int cmd,
+                                 size_t size,
+                                 size_t count,
+                                 FAR struct virtio_snd_pcm_info *info)
+{
+  FAR struct virtio_snd_query_info *req;
+  FAR struct virtio_snd_hdr *resp;
+  struct virtqueue_buf vb[3];
+  int ret;
+
+  req = virtio_zalloc_buf(priv->vdev, sizeof(*req), 16);
+  if (req == NULL)
+    {
+      vrterr("virtio audio driver cmd request alloc failed\n");
+      return -ENOMEM;
+    }
+
+  req->hdr.code = cmd;
+  req->count = count;
+  req->size = size;
+
+  resp = virtio_alloc_buf(priv->vdev, sizeof(*resp), 16);
+  if (resp == NULL)
+    {
+      vrterr("virtio audio driver cmd response alloc failed\n");
+      ret = -ENOMEM;
+      goto out;
+    }
+
+  resp->code = VIRTIO_SND_S_IO_ERR;
+
+  vb[0].buf = req;
+  vb[0].len = sizeof(*req);
+  vb[1].buf = resp;
+  vb[1].len = sizeof(*resp);
+  vb[2].buf = info;
+  vb[2].len = count * size;
+
+  ret = virtio_snd_send_ctl(priv, vb, 1, 2);
+  if (ret < 0)
+    {
+      vrterr("send msg error:%d\n", ret);
+      goto out;
+    }
+
+  ret = resp->code == VIRTIO_SND_S_OK ? OK : -EIO;
+  vrtinfo("send cmd:0x%x and resp:%d\n", cmd, ret);
+
+out:
+  virtio_free_buf(priv->vdev, req);
+  virtio_free_buf(priv->vdev, resp);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_dev_set_params
+ ****************************************************************************/
+
+static int virtio_snd_set_params(FAR struct virtio_snd_dev_s *sdev,
+                                 unsigned int ch,
+                                 unsigned int rate,
+                                 unsigned int bps)
+{
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_snd_pcm_set_params *req;
+  FAR struct virtio_snd_hdr *resp;
+  struct virtqueue_buf vb[2];
+  size_t i;
+  int ret;
+
+  req = virtio_zalloc_buf(priv->vdev, sizeof(*req), 16);
+  if (req == NULL)
+    {
+      vrterr("zalloc for request error\n");
+      return -ENOMEM;
+    }
+
+  req->hdr.hdr.code = VIRTIO_SND_R_PCM_SET_PARAMS;
+  req->hdr.stream_id = sdev->index;
+  req->channels = ch;
+
+  req->rate = VIRTIO_SND_PCM_RATE_44100;
+  for (i = 0; i < nitems(g_rate_map); i++)
+    {
+      if (rate == g_rate_map[i].sps)
+        {
+          req->rate = i;
+          break;
+        }
+    }
+
+  req->format = VIRTIO_SND_PCM_FMT_S16;
+  for (i = 0; i < nitems(g_format_map); i++)
+    {
+      if (bps == g_format_map[i].bps)
+        {
+          req->format = i;
+          break;
+        }
+    }
+
+  req->period_bytes = sdev->period_bytes;
+  req->buffer_bytes = req->period_bytes *
+                      CONFIG_DRIVERS_VIRTIO_SND_BUFFER_COUNT;
+
+  resp = virtio_alloc_buf(priv->vdev, sizeof(*resp), 16);
+  if (resp == NULL)
+    {
+      vrterr("zalloc for request error\n");
+      ret = -ENOMEM;
+      goto out;
+    }
+
+  resp->code = VIRTIO_SND_S_IO_ERR;
+
+  vb[0].buf = req;
+  vb[0].len = sizeof(*req);
+  vb[1].buf = resp;
+  vb[1].len = sizeof(*resp);
+
+  ret = virtio_snd_send_ctl(priv, vb, 1, 1);
+  if (ret < 0)
+    {
+      vrterr("send msg error:%d\n", ret);
+      goto out;
+    }
+
+  ret = resp->code == VIRTIO_SND_S_OK ? OK : -EIO;
+
+  vrtinfo("send cmd:0x%x and resp:%d\n",
+          VIRTIO_SND_R_PCM_SET_PARAMS, ret);
+
+out:
+  virtio_free_buf(priv->vdev, req);
+  virtio_free_buf(priv->vdev, resp);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_send_cmd
+ ****************************************************************************/
+
+static int virtio_snd_send_cmd(FAR struct virtio_snd_dev_s *sdev,
+                               int cmd)
+{
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_device *vdev = priv->vdev;
+  FAR struct virtio_snd_pcm_hdr *req;
+  FAR struct virtio_snd_hdr *resp;
+  struct virtqueue_buf vb[2];
+  int ret;
+
+  req = virtio_alloc_buf(vdev, sizeof(*req), 16);
+  if (req == NULL)
+    {
+      vrterr("zalloc for request error\n");
+      return -ENOMEM;
+    }
+
+  req->hdr.code = cmd;
+  req->stream_id = sdev->index;
+
+  resp = virtio_alloc_buf(vdev, sizeof(*resp), 16);
+  if (resp == NULL)
+    {
+      vrterr("zalloc for request error\n");
+      ret = -ENOMEM;
+      goto out;
+    }
+
+  resp->code = VIRTIO_SND_S_IO_ERR;
+
+  vb[0].buf = req;
+  vb[0].len = sizeof(*req);
+  vb[1].buf = resp;
+  vb[1].len = sizeof(*resp);
+
+  ret = virtio_snd_send_ctl(priv, vb, 1, 1);
+  if (ret < 0)
+    {
+      vrterr("send msg error:%d\n", ret);
+      goto out;
+    }
+
+  ret = resp->code == VIRTIO_SND_S_OK ? OK : -EIO;
+  if (ret < 0)
+    {
+      vrterr("check response error:%d\n", ret);
+    }
+
+  vrtinfo("send cmd:0x%x and resp:%d\n", cmd, ret);
+
+out:
+  virtio_free_buf(vdev, req);
+  virtio_free_buf(vdev, resp);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_getcaps
+ ****************************************************************************/
+
+static int virtio_snd_getcaps(FAR struct audio_lowerhalf_s *dev,
+                              int type,
+                              FAR struct audio_caps_s *caps)
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_snd_pcm_info *info = &priv->info[sdev->index];
+
+  DEBUGASSERT(caps->ac_len >= sizeof(struct audio_caps_s));
+
+  caps->ac_format.hw  = 0;
+  caps->ac_controls.w = 0;
+
+  switch (caps->ac_type)
+    {
+      case AUDIO_TYPE_QUERY:
+        switch (caps->ac_subtype)
+          {
+            case AUDIO_TYPE_QUERY:
+              caps->ac_controls.b[0] =
+              info->direction == VIRTIO_SND_D_INPUT ?
+                                 AUDIO_TYPE_INPUT : AUDIO_TYPE_OUTPUT;
+              caps->ac_format.hw = 1 << (AUDIO_FMT_PCM - 1);
+              break;
+
+            case AUDIO_FMT_PCM:
+              virtio_snd_get_support_formats(info, caps);
+              break;
+
+            default:
+              caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
+              break;
+          }
+         break;
+
+      case AUDIO_TYPE_OUTPUT:
+      case AUDIO_TYPE_INPUT:
+        {
+          caps->ac_channels = (info->channels_min << 4) |
+                              (info->channels_max & 0x0f);
+          switch (caps->ac_subtype)
+            {
+              case AUDIO_TYPE_QUERY:
+                caps->ac_controls.b[0] = virtio_snd_get_support_rates(info);
+                break;
+
+              default:
+                break;
+            }
+          break;
+        }
+
+      default:
+        caps->ac_subtype = 0;
+        caps->ac_channels = 0;
+        break;
+    }
+
+  return caps->ac_len;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_configure
+ *
+ * Description:
+ *   Configure the audio device for the specified  mode of operation.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_configure(FAR struct audio_lowerhalf_s *dev,
+                                FAR void *session,
+                                FAR const struct audio_caps_s *caps)
+#else
+static int virtio_snd_configure(FAR struct audio_lowerhalf_s *dev,
+                                FAR const struct audio_caps_s *caps)
+#endif
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  int ret = -ENOTTY;
+  uint32_t rate;
+  uint8_t bps;
+  uint8_t ch;
+
+  switch (caps->ac_type)
+    {
+      case AUDIO_TYPE_OUTPUT:
+      case AUDIO_TYPE_INPUT:
+        vrtinfo("Number of channels: %"PRIu8"\n", caps->ac_channels);
+        vrtinfo("Sample rate:        %"PRIu16"\n", caps->ac_controls.hw[0]);
+        vrtinfo("Sample width:       %"PRIu8"\n", caps->ac_controls.b[2]);
+        vrtinfo("channel map: 0x%x\n", caps->ac_chmap);
+        rate = caps->ac_controls.hw[0] | (caps->ac_controls.b[3] << 16);
+        bps = caps->ac_controls.b[2];
+        ch = caps->ac_channels;
+        sdev->period_bytes =
+        virtio_snd_get_period_bytes(rate, ch, bps,
+                                    CONFIG_DRIVERS_VIRTIO_SOUND_PERIOD_TIME);
+        vrtinfo("period_bytes:%"PRIu32"\n", sdev->period_bytes);
+        ret = virtio_snd_set_params(sdev, ch, rate, bps);
+        if (ret < 0)
+          {
+            break;
+          }
+
+        ret = virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_PREPARE);
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_start
+ *
+ * Description:
+ *   Start the configured operation (audio streaming, volume enabled, etc.).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_start(FAR struct audio_lowerhalf_s *dev,
+                            FAR void *session)
+#else
+static int virtio_snd_start(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+
+  return virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_START);
+}
+
+/****************************************************************************
+ * Name: virtio_snd_stop
+ *
+ * Description: Stop the configured operation (audio streaming, volume
+ *              disabled, etc.).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_stop(FAR struct audio_lowerhalf_s *dev,
+                           FAR void *session)
+#else
+static int virtio_snd_stop(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+
+  virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_STOP);
+  virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_RELEASE);
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+  dev->upper(dev->priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
+#else
+  dev->upper(dev->priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
+#endif
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: virtio_snd_pause
+ *
+ * Description: Pauses the playback.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_pause(FAR struct audio_lowerhalf_s *dev,
+                            FAR void *session)
+#else
+static int virtio_snd_pause(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+
+  return virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_STOP);
+}
+#endif
+
+/****************************************************************************
+ * Name: virtio_snd_resume
+ *
+ * Description: Resumes the playback.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_resume(FAR struct audio_lowerhalf_s *dev,
+                             FAR void *session)
+#else
+static int virtio_snd_resume(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+
+  return virtio_snd_send_cmd(sdev, VIRTIO_SND_R_PCM_START);
+}
+#endif
+
+static int virtio_snd_allocbuffer(FAR struct audio_lowerhalf_s *dev,
+                                  FAR struct audio_buf_desc_s *desc)
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_snd_buffer_s *buf;
+
+  DEBUGASSERT(desc->u.pbuffer != NULL);
+
+  buf = virtio_zalloc_buf(priv->vdev, sizeof(*buf) + desc->numbytes, 16);
+  if (buf == NULL)
+    {
+      vrterr("failed to allocate apb buffer\n");
+      return -ENOMEM;
+    }
+
+  *desc->u.pbuffer = &buf->apb;
+
+  buf->apb.crefs = 1;
+  buf->apb.nmaxbytes = desc->numbytes;
+  buf->apb.samp = (FAR uint8_t *)(buf + 1);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+  buf->apb.session = desc->session;
+#endif
+  buf->xfer.stream_id = sdev->index;
+  buf->status.status = VIRTIO_SND_S_IO_ERR;
+  buf->dev = dev;
+  nxmutex_init(&buf->apb.lock);
+
+  return sizeof(struct audio_buf_desc_s);
+}
+
+static int virtio_snd_freebuffer(FAR struct audio_lowerhalf_s *dev,
+                                 FAR struct audio_buf_desc_s *desc)
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  FAR struct virtio_snd_s *priv = sdev->priv;
+  FAR struct virtio_device *vdev = priv->vdev;
+  FAR struct ap_buffer_s *apb = desc->u.buffer;
+  int refcount;
+
+  nxmutex_lock(&apb->lock);
+  refcount = apb->crefs--;
+  nxmutex_unlock(&apb->lock);
+  if (refcount <= 1)
+    {
+      nxmutex_destroy(&apb->lock);
+      virtio_free_buf(vdev, apb);
+    }
+
+  return sizeof(struct audio_buf_desc_s);
+}
+
+/****************************************************************************
+ * Name: virtio_snd_enqueuebuffer
+ *
+ * Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
+ *
+ ****************************************************************************/
+
+static int virtio_snd_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
+                                    FAR struct ap_buffer_s *apb)
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  FAR struct virtio_snd_buffer_s *buf =
+    (FAR struct virtio_snd_buffer_s *)apb;
+
+  return virtio_snd_send_pcm(sdev, buf);
+}
+
+/****************************************************************************
+ * Name: virtio_snd_ioctl
+ *
+ * Description: Perform a device ioctl
+ *
+ ****************************************************************************/
+
+static int virtio_snd_ioctl(FAR struct audio_lowerhalf_s *dev,
+                            int cmd,
+                            unsigned long arg)
+{
+  FAR struct virtio_snd_dev_s *sdev = (FAR struct virtio_snd_dev_s *)dev;
+  FAR struct ap_buffer_info_s *bufinfo;
+
+  switch (cmd)
+    {
+      case AUDIOIOC_GETBUFFERINFO:
+        bufinfo = (FAR struct ap_buffer_info_s *)arg;
+        bufinfo->nbuffers = CONFIG_DRIVERS_VIRTIO_SND_BUFFER_COUNT;
+        bufinfo->buffer_size = sdev->period_bytes;
+        break;
+
+      default:
+        return -ENOTTY;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_shutdown
+ *
+ * Description:
+ *   Shutdown the driver and put it in the lowest power state possible.
+ *
+ ****************************************************************************/
+
+static int virtio_snd_shutdown(FAR struct audio_lowerhalf_s *dev)
+{
+  vrtinfo("Return OK\n");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_reserve
+ *
+ * Description: Reserves a session (the only one we have).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_reserve(FAR struct audio_lowerhalf_s *dev,
+                              FAR void **session)
+#else
+static int virtio_snd_reserve(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  vrtinfo("Return OK\n");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_release
+ *
+ * Description: Releases the session (the only one we have).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int virtio_snd_release(FAR struct audio_lowerhalf_s *dev,
+                              FAR void *session)
+#else
+static int virtio_snd_release(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  vrtinfo("Return OK\n");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_init
+ ****************************************************************************/
+
+static int virtio_snd_init(FAR struct virtio_snd_s *priv)
+{
+  FAR const char *vqnames[VIRTIO_SND_VQ_MAX];
+  vq_callback callbacks[VIRTIO_SND_VQ_MAX];
+  int ret;
+  int i;
+
+  vqnames[VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl";
+  vqnames[VIRTIO_SND_VQ_EVENT] = "virtsnd-event";
+  vqnames[VIRTIO_SND_VQ_TX] = "virtsnd-tx";
+  vqnames[VIRTIO_SND_VQ_RX] = "virtsnd-rx";
+
+  callbacks[VIRTIO_SND_VQ_CONTROL] = virtio_snd_ctl_notify_cb;
+  callbacks[VIRTIO_SND_VQ_EVENT] = virtio_snd_event_notify_cb;
+  callbacks[VIRTIO_SND_VQ_TX] = virtio_snd_pcm_notify_cb;
+  callbacks[VIRTIO_SND_VQ_RX] = virtio_snd_pcm_notify_cb;
+
+  ret = virtio_create_virtqueues(priv->vdev, 0,
+                                 VIRTIO_SND_VQ_MAX, vqnames, callbacks);
+  if (ret < 0)
+    {
+      vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
+      return ret;
+    }
+
+  for (i = 0; i < VIRTIO_SND_VQ_MAX; i++)
+    {
+      virtqueue_enable_cb(priv->vdev->vrings_info[i].vq);
+    }
+
+  virtio_set_status(priv->vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+
+  virtio_read_config(priv->vdev, 0, &priv->config,
+                     sizeof(struct virtio_snd_config));
+  vrtinfo("jacks:%"PRIu32" streams:%"PRIu32" chmap:%"PRIu32"\n",
+           priv->config.jacks, priv->config.streams, priv->config.chmaps);
+
+  priv->info = virtio_alloc_buf(priv->vdev,
+                                priv->config.streams *
+                                sizeof(struct virtio_snd_pcm_info),
+                                16);
+  if (priv->info == NULL)
+    {
+      vrterr("virtio audio driver query pcm info alloc failed\n");
+      ret = -ENOMEM;
+      goto err_with_vq;
+    }
+
+  /* Query pcm info */
+
+  ret = virtio_snd_query_info(priv, VIRTIO_SND_R_PCM_INFO,
+                              sizeof(struct virtio_snd_pcm_info),
+                              priv->config.streams,
+                              priv->info);
+  if (ret < 0)
+    {
+      vrterr("virtio snd query pcm info failed,ret:%d\n", ret);
+      goto err_with_info;
+    }
+
+  return ret;
+
+err_with_info:
+  virtio_free_buf(priv->vdev, priv->info);
+err_with_vq:
+  virtio_delete_virtqueues(priv->vdev);
+  return ret;
+}
+
+static int
+virtio_snd_register_audio_driver(FAR struct virtio_snd_s *priv)
+{
+  char devname[32];
+  int tx_minor = 0;
+  int rx_minor = 0;
+  int ret = -ENODEV;
+  uint32_t i;
+
+  priv->dev = kmm_zalloc(priv->config.streams *
+                         sizeof(struct virtio_snd_dev_s));
+  if (priv->dev == NULL)
+    {
+      vrterr("zalloc for virtio audio device failed\n");
+      return -ENOMEM;
+    }
+
+  for (i = 0; i < priv->config.streams; i++)
+    {
+      switch (priv->info[i].direction)
+        {
+          case VIRTIO_SND_D_OUTPUT:
+            snprintf(devname, 32, "pcm%dp", tx_minor++);
+            break;
+
+          case VIRTIO_SND_D_INPUT:
+            snprintf(devname, 32, "pcm%dc", rx_minor++);
+            break;
+        }
+
+      priv->dev[i].index = i;
+      priv->dev[i].priv = priv;
+      priv->dev[i].dev.ops = &g_virtio_snd_ops;
+      ret = audio_register(devname, &priv->dev[i].dev);
+      if (ret < 0)
+        {
+          vrterr("failed to register /dev/%s device: %d\n",
+                  devname, ret);
+          break;
+        }
+    }
+
+  if (i == 0)
+    {
+      kmm_free(priv->dev);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_probe
+ ****************************************************************************/
+
+static int virtio_snd_probe(FAR struct virtio_device *vdev)
+{
+  FAR struct virtio_snd_s *priv;
+  int ret;
+
+  priv = kmm_zalloc(sizeof(*priv));
+  if (priv == NULL)
+    {
+      vrterr("virtio net driver priv alloc failed\n");
+      return -ENOMEM;
+    }
+
+  priv->vdev = vdev;
+  vdev->priv = priv;
+
+  ret = virtio_snd_init(priv);
+  if (ret < 0)
+    {
+      goto err_with_virtsnd;
+    }
+
+  ret = virtio_snd_register_audio_driver(priv);
+  if (ret < 0)
+    {
+      goto err_with_vq;
+    }
+
+  return ret;
+
+err_with_vq:
+  virtio_reset_device(vdev);
+  virtio_delete_virtqueues(vdev);
+  virtio_free_buf(vdev, priv->info);
+err_with_virtsnd:
+  kmm_free(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_snd_remove
+ ****************************************************************************/
+
+static void virtio_snd_remove(FAR struct virtio_device *vdev)
+{
+  FAR struct virtio_snd_s *priv = vdev->priv;
+
+  virtio_reset_device(vdev);
+  virtio_delete_virtqueues(vdev);
+  virtio_free_buf(vdev, priv->info);
+  kmm_free(priv->dev);
+  kmm_free(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtio_register_snd_driver
+ ****************************************************************************/
+
+int virtio_register_snd_driver(void)
+{
+  return virtio_register_driver(&g_virtio_snd_driver);
+}
diff --git a/drivers/virtio/virtio-snd.h b/drivers/virtio/virtio-snd.h
new file mode 100644
index 0000000000..8d22f52bbe
--- /dev/null
+++ b/drivers/virtio/virtio-snd.h
@@ -0,0 +1,362 @@
+/****************************************************************************
+ * drivers/virtio/virtio-snd.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_VIRTIO_VIRTIO_SOUND_H
+#define __DRIVERS_VIRTIO_VIRTIO_SOUND_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+#ifdef CONFIG_DRIVERS_VIRTIO_SOUND
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define VIRTIO_SND_CHMAP_MAX_SIZE 18
+
+enum
+{
+  /* Device virtqueue indexes */
+
+  VIRTIO_SND_VQ_CONTROL = 0,
+  VIRTIO_SND_VQ_EVENT,
+  VIRTIO_SND_VQ_TX,
+  VIRTIO_SND_VQ_RX,
+
+  /* Of device virtqueues */
+
+  VIRTIO_SND_VQ_MAX
+};
+
+/* Supported dataflow directions */
+
+enum
+{
+  VIRTIO_SND_D_OUTPUT = 0,
+  VIRTIO_SND_D_INPUT
+};
+
+enum
+{
+  /* Jack control request types */
+
+  VIRTIO_SND_R_JACK_INFO = 1,
+  VIRTIO_SND_R_JACK_REMAP,
+
+  /* Pcm control request types */
+
+  VIRTIO_SND_R_PCM_INFO = 0x0100,
+  VIRTIO_SND_R_PCM_SET_PARAMS,
+  VIRTIO_SND_R_PCM_PREPARE,
+  VIRTIO_SND_R_PCM_RELEASE,
+  VIRTIO_SND_R_PCM_START,
+  VIRTIO_SND_R_PCM_STOP,
+
+  /* Channel map control request types */
+
+  VIRTIO_SND_R_CHMAP_INFO = 0x0200,
+
+  /* Jack event types */
+
+  VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000,
+  VIRTIO_SND_EVT_JACK_DISCONNECTED,
+
+  /* Pcm event types */
+
+  VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100,
+  VIRTIO_SND_EVT_PCM_XRUN,
+
+  /* Common status codes */
+
+  VIRTIO_SND_S_OK = 0x8000,
+  VIRTIO_SND_S_BAD_MSG,
+  VIRTIO_SND_S_NOT_SUPP,
+  VIRTIO_SND_S_IO_ERR
+};
+
+/* Virtio snd configuration */
+
+struct virtio_snd_config
+{
+  uint32_t jacks;
+  uint32_t streams;
+  uint32_t chmaps;
+};
+
+/* Common header */
+
+struct virtio_snd_hdr
+{
+  uint32_t code;
+};
+
+/* Event notification */
+
+struct virtio_snd_event
+{
+  struct virtio_snd_hdr hdr;
+  uint32_t data;
+};
+
+/* Common control request to query an item information */
+
+struct virtio_snd_query_info
+{
+  struct virtio_snd_hdr hdr;
+  uint32_t start_id;
+  uint32_t count;
+  uint32_t size;
+};
+
+/* Common item information header */
+
+struct virtio_snd_info
+{
+  uint32_t hda_fn_nid;
+};
+
+/* Jack control messages */
+
+struct virtio_snd_jack_hdr
+{
+  struct virtio_snd_hdr hdr;
+  uint32_t jack_id;
+};
+
+/* Supported jack features */
+
+enum
+{
+  VIRTIO_SND_JACK_F_REMAP = 0
+};
+
+/* Query jack info,device->driver */
+
+struct virtio_snd_jack_info
+{
+  struct virtio_snd_info hdr;
+  uint32_t features;
+  uint32_t hda_reg_defconf;
+  uint32_t hda_reg_caps;
+  uint8_t connected;
+  uint8_t padding[7];
+};
+
+/* Jack remapping control request */
+
+struct virtio_snd_jack_remap
+{
+  struct virtio_snd_jack_hdr hdr;
+  uint32_t association;
+  uint32_t sequence;
+};
+
+/* Pcm control messages */
+
+struct virtio_snd_pcm_hdr
+{
+  struct virtio_snd_hdr hdr;
+  uint32_t stream_id;
+};
+
+/* Supported PCM stream features */
+
+enum
+{
+  VIRTIO_SND_PCM_F_SHMEM_HOST = 0,
+  VIRTIO_SND_PCM_F_SHMEM_GUEST,
+  VIRTIO_SND_PCM_F_MSG_POLLING,
+  VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS,
+  VIRTIO_SND_PCM_F_EVT_XRUNS
+};
+
+/* Supported PCM sample formats */
+
+enum
+{
+  VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0,  /*  4 /  4 bits */
+  VIRTIO_SND_PCM_FMT_MU_LAW,         /*  8 /  8 bits */
+  VIRTIO_SND_PCM_FMT_A_LAW,          /*  8 /  8 bits */
+  VIRTIO_SND_PCM_FMT_S8,             /*  8 /  8 bits */
+  VIRTIO_SND_PCM_FMT_U8,             /*  8 /  8 bits */
+  VIRTIO_SND_PCM_FMT_S16,            /* 16 / 16 bits */
+  VIRTIO_SND_PCM_FMT_U16,            /* 16 / 16 bits */
+  VIRTIO_SND_PCM_FMT_S18_3,          /* 18 / 24 bits */
+  VIRTIO_SND_PCM_FMT_U18_3,          /* 18 / 24 bits */
+  VIRTIO_SND_PCM_FMT_S20_3,          /* 20 / 24 bits */
+  VIRTIO_SND_PCM_FMT_U20_3,          /* 20 / 24 bits */
+  VIRTIO_SND_PCM_FMT_S24_3,          /* 24 / 24 bits */
+  VIRTIO_SND_PCM_FMT_U24_3,          /* 24 / 24 bits */
+  VIRTIO_SND_PCM_FMT_S20,            /* 20 / 32 bits */
+  VIRTIO_SND_PCM_FMT_U20,            /* 20 / 32 bits */
+  VIRTIO_SND_PCM_FMT_S24,            /* 24 / 32 bits */
+  VIRTIO_SND_PCM_FMT_U24,            /* 24 / 32 bits */
+  VIRTIO_SND_PCM_FMT_S32,            /* 32 / 32 bits */
+  VIRTIO_SND_PCM_FMT_U32,            /* 32 / 32 bits */
+  VIRTIO_SND_PCM_FMT_FLOAT,          /* 32 / 32 bits */
+  VIRTIO_SND_PCM_FMT_FLOAT64,        /* 64 / 64 bits */
+  VIRTIO_SND_PCM_FMT_DSD_U8,         /*  8 /  8 bits */
+  VIRTIO_SND_PCM_FMT_DSD_U16,        /* 16 / 16 bits */
+  VIRTIO_SND_PCM_FMT_DSD_U32,        /* 32 / 32 bits */
+  VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */
+};
+
+/* Supported PCM frame rates */
+
+enum
+{
+  VIRTIO_SND_PCM_RATE_5512 = 0,
+  VIRTIO_SND_PCM_RATE_8000,
+  VIRTIO_SND_PCM_RATE_11025,
+  VIRTIO_SND_PCM_RATE_16000,
+  VIRTIO_SND_PCM_RATE_22050,
+  VIRTIO_SND_PCM_RATE_32000,
+  VIRTIO_SND_PCM_RATE_44100,
+  VIRTIO_SND_PCM_RATE_48000,
+  VIRTIO_SND_PCM_RATE_64000,
+  VIRTIO_SND_PCM_RATE_88200,
+  VIRTIO_SND_PCM_RATE_96000,
+  VIRTIO_SND_PCM_RATE_176400,
+  VIRTIO_SND_PCM_RATE_192000,
+  VIRTIO_SND_PCM_RATE_384000
+};
+
+/* Query pcm info,device->driver */
+
+struct virtio_snd_pcm_info
+{
+  struct virtio_snd_info hdr;
+  uint32_t features;
+  uint64_t formats;
+  uint64_t rates;
+  uint8_t direction;
+  uint8_t channels_min;
+  uint8_t channels_max;
+  uint8_t padding[5];
+};
+
+/* Set pcm stream format */
+
+struct virtio_snd_pcm_set_params
+{
+  struct virtio_snd_pcm_hdr hdr;
+  uint32_t buffer_bytes;
+  uint32_t period_bytes;
+  uint32_t features;
+  uint8_t channels;
+  uint8_t format;
+  uint8_t rate;
+  uint8_t padding;
+};
+
+/* I/O request header */
+
+struct virtio_snd_pcm_xfer
+{
+  uint32_t stream_id;
+};
+
+/* I/O request status */
+
+struct virtio_snd_pcm_status
+{
+  uint32_t status;
+  uint32_t latency_bytes;
+};
+
+/* Standard channel position definition */
+
+enum
+{
+  VIRTIO_SND_CHMAP_NONE = 0,    /* Undefined */
+  VIRTIO_SND_CHMAP_NA,          /* Silent */
+  VIRTIO_SND_CHMAP_MONO,        /* Mono stream */
+  VIRTIO_SND_CHMAP_FL,          /* Front left */
+  VIRTIO_SND_CHMAP_FR,          /* Front right */
+  VIRTIO_SND_CHMAP_RL,          /* Rear left */
+  VIRTIO_SND_CHMAP_RR,          /* Rear right */
+  VIRTIO_SND_CHMAP_FC,          /* Front center */
+  VIRTIO_SND_CHMAP_LFE,         /* Low frequency (LFE) */
+  VIRTIO_SND_CHMAP_SL,          /* Side left */
+  VIRTIO_SND_CHMAP_SR,          /* Side right */
+  VIRTIO_SND_CHMAP_RC,          /* Rear center */
+  VIRTIO_SND_CHMAP_FLC,         /* Front left center */
+  VIRTIO_SND_CHMAP_FRC,         /* Front right center */
+  VIRTIO_SND_CHMAP_RLC,         /* Rear left center */
+  VIRTIO_SND_CHMAP_RRC,         /* Rear right center */
+  VIRTIO_SND_CHMAP_FLW,         /* Front left wide */
+  VIRTIO_SND_CHMAP_FRW,         /* Front right wide */
+  VIRTIO_SND_CHMAP_FLH,         /* Front left high */
+  VIRTIO_SND_CHMAP_FCH,         /* Front center high */
+  VIRTIO_SND_CHMAP_FRH,         /* Front right high */
+  VIRTIO_SND_CHMAP_TC,          /* Top center */
+  VIRTIO_SND_CHMAP_TFL,         /* Top front left */
+  VIRTIO_SND_CHMAP_TFR,         /* Top front right */
+  VIRTIO_SND_CHMAP_TFC,         /* Top front center */
+  VIRTIO_SND_CHMAP_TRL,         /* Top rear left */
+  VIRTIO_SND_CHMAP_TRR,         /* Top rear right */
+  VIRTIO_SND_CHMAP_TRC,         /* Top rear center */
+  VIRTIO_SND_CHMAP_TFLC,        /* Top front left center */
+  VIRTIO_SND_CHMAP_TFRC,        /* Top front right center */
+  VIRTIO_SND_CHMAP_TSL,         /* Top side left */
+  VIRTIO_SND_CHMAP_TSR,         /* Top side right */
+  VIRTIO_SND_CHMAP_LLFE,        /* Left LFE */
+  VIRTIO_SND_CHMAP_RLFE,        /* Right LFE */
+  VIRTIO_SND_CHMAP_BC,          /* Bottom center */
+  VIRTIO_SND_CHMAP_BLC,         /* Bottom left center */
+  VIRTIO_SND_CHMAP_BRC          /* Bottom right center */
+};
+
+/* Query channel map info,device->driver */
+
+struct virtio_snd_chmap_info
+{
+  struct virtio_snd_info hdr;
+  uint8_t direction;
+  uint8_t channels;
+  uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+int virtio_register_snd_driver(void);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_DRIVERS_VIRTIO_SOUND */
+#endif /* __DRIVERS_VIRTIO_VIRTIO_SOUND_H */
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index d9db6c0304..2508267be1 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -33,6 +33,7 @@
 #include "virtio-net.h"
 #include "virtio-rng.h"
 #include "virtio-serial.h"
+#include "virtio-snd.h"
 
 /****************************************************************************
  * Private Types
@@ -154,7 +155,15 @@ void virtio_register_drivers(void)
   ret = virtio_register_serial_driver();
   if (ret < 0)
     {
-      vrterr("virtio_serial_driver_init failed, ret=%d\n", ret);
+      vrterr("virtio_register_serial_driver failed, ret=%d\n", ret);
+    }
+#endif
+
+#ifdef CONFIG_DRIVERS_VIRTIO_SOUND
+  ret = virtio_register_snd_driver();
+  if (ret < 0)
+    {
+      vrterr("virtio_register_snd_driver failed, ret=%d\n", ret);
     }
 #endif