diff --git a/drivers/virtio/CMakeLists.txt b/drivers/virtio/CMakeLists.txt index ebd9ea25c5b..9d9097e7ea5 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 76c24d90d6c..fd74fee3692 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 20df7ce537e..fd257163ba2 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 00000000000..22d3b2c870e --- /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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 00000000000..8d22f52bbe4 --- /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 +#include + +#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 d9db6c0304b..2508267be14 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