455 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * virtio-snd: Virtio sound device4 * Copyright (C) 2021 OpenSynergy GmbH5 */6#include <linux/module.h>7#include <linux/moduleparam.h>8#include <linux/virtio_config.h>9#include <sound/initval.h>10#include <uapi/linux/virtio_ids.h>11 12#include "virtio_card.h"13 14u32 virtsnd_msg_timeout_ms = MSEC_PER_SEC;15module_param_named(msg_timeout_ms, virtsnd_msg_timeout_ms, uint, 0644);16MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds");17 18static void virtsnd_remove(struct virtio_device *vdev);19 20/**21 * virtsnd_event_send() - Add an event to the event queue.22 * @vqueue: Underlying event virtqueue.23 * @event: Event.24 * @notify: Indicates whether or not to send a notification to the device.25 * @gfp: Kernel flags for memory allocation.26 *27 * Context: Any context.28 */29static void virtsnd_event_send(struct virtqueue *vqueue,30 struct virtio_snd_event *event, bool notify,31 gfp_t gfp)32{33 struct scatterlist sg;34 struct scatterlist *psgs[1] = { &sg };35 36 /* reset event content */37 memset(event, 0, sizeof(*event));38 39 sg_init_one(&sg, event, sizeof(*event));40 41 if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)42 return;43 44 if (virtqueue_kick_prepare(vqueue))45 virtqueue_notify(vqueue);46}47 48/**49 * virtsnd_event_dispatch() - Dispatch an event from the device side.50 * @snd: VirtIO sound device.51 * @event: VirtIO sound event.52 *53 * Context: Any context.54 */55static void virtsnd_event_dispatch(struct virtio_snd *snd,56 struct virtio_snd_event *event)57{58 switch (le32_to_cpu(event->hdr.code)) {59 case VIRTIO_SND_EVT_JACK_CONNECTED:60 case VIRTIO_SND_EVT_JACK_DISCONNECTED:61 virtsnd_jack_event(snd, event);62 break;63 case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:64 case VIRTIO_SND_EVT_PCM_XRUN:65 virtsnd_pcm_event(snd, event);66 break;67 case VIRTIO_SND_EVT_CTL_NOTIFY:68 virtsnd_kctl_event(snd, event);69 break;70 }71}72 73/**74 * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.75 * @vqueue: Underlying event virtqueue.76 *77 * This callback function is called upon a vring interrupt request from the78 * device.79 *80 * Context: Interrupt context.81 */82static void virtsnd_event_notify_cb(struct virtqueue *vqueue)83{84 struct virtio_snd *snd = vqueue->vdev->priv;85 struct virtio_snd_queue *queue = virtsnd_event_queue(snd);86 struct virtio_snd_event *event;87 u32 length;88 unsigned long flags;89 90 spin_lock_irqsave(&queue->lock, flags);91 do {92 virtqueue_disable_cb(vqueue);93 while ((event = virtqueue_get_buf(vqueue, &length))) {94 virtsnd_event_dispatch(snd, event);95 virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);96 }97 } while (!virtqueue_enable_cb(vqueue));98 spin_unlock_irqrestore(&queue->lock, flags);99}100 101/**102 * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.103 * @snd: VirtIO sound device.104 *105 * After calling this function, the event queue is disabled.106 *107 * Context: Any context.108 * Return: 0 on success, -errno on failure.109 */110static int virtsnd_find_vqs(struct virtio_snd *snd)111{112 struct virtio_device *vdev = snd->vdev;113 struct virtqueue_info vqs_info[VIRTIO_SND_VQ_MAX] = {114 [VIRTIO_SND_VQ_CONTROL] = { "virtsnd-ctl",115 virtsnd_ctl_notify_cb },116 [VIRTIO_SND_VQ_EVENT] = { "virtsnd-event",117 virtsnd_event_notify_cb },118 [VIRTIO_SND_VQ_TX] = { "virtsnd-tx",119 virtsnd_pcm_tx_notify_cb },120 [VIRTIO_SND_VQ_RX] = { "virtsnd-rx",121 virtsnd_pcm_rx_notify_cb },122 };123 struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };124 unsigned int i;125 unsigned int n;126 int rc;127 128 rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, vqs_info, NULL);129 if (rc) {130 dev_err(&vdev->dev, "failed to initialize virtqueues\n");131 return rc;132 }133 134 for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)135 snd->queues[i].vqueue = vqs[i];136 137 /* Allocate events and populate the event queue */138 virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);139 140 n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);141 142 snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),143 GFP_KERNEL);144 if (!snd->event_msgs)145 return -ENOMEM;146 147 for (i = 0; i < n; ++i)148 virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],149 &snd->event_msgs[i], false, GFP_KERNEL);150 151 return 0;152}153 154/**155 * virtsnd_enable_event_vq() - Enable the event virtqueue.156 * @snd: VirtIO sound device.157 *158 * Context: Any context.159 */160static void virtsnd_enable_event_vq(struct virtio_snd *snd)161{162 struct virtio_snd_queue *queue = virtsnd_event_queue(snd);163 164 if (!virtqueue_enable_cb(queue->vqueue))165 virtsnd_event_notify_cb(queue->vqueue);166}167 168/**169 * virtsnd_disable_event_vq() - Disable the event virtqueue.170 * @snd: VirtIO sound device.171 *172 * Context: Any context.173 */174static void virtsnd_disable_event_vq(struct virtio_snd *snd)175{176 struct virtio_snd_queue *queue = virtsnd_event_queue(snd);177 struct virtio_snd_event *event;178 u32 length;179 unsigned long flags;180 181 if (queue->vqueue) {182 spin_lock_irqsave(&queue->lock, flags);183 virtqueue_disable_cb(queue->vqueue);184 while ((event = virtqueue_get_buf(queue->vqueue, &length)))185 virtsnd_event_dispatch(snd, event);186 spin_unlock_irqrestore(&queue->lock, flags);187 }188}189 190/**191 * virtsnd_build_devs() - Read configuration and build ALSA devices.192 * @snd: VirtIO sound device.193 *194 * Context: Any context that permits to sleep.195 * Return: 0 on success, -errno on failure.196 */197static int virtsnd_build_devs(struct virtio_snd *snd)198{199 struct virtio_device *vdev = snd->vdev;200 struct device *dev = &vdev->dev;201 int rc;202 203 rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,204 THIS_MODULE, 0, &snd->card);205 if (rc < 0)206 return rc;207 208 snd->card->private_data = snd;209 210 strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,211 sizeof(snd->card->driver));212 strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,213 sizeof(snd->card->shortname));214 if (dev->parent->bus)215 snprintf(snd->card->longname, sizeof(snd->card->longname),216 VIRTIO_SND_CARD_NAME " at %s/%s/%s",217 dev->parent->bus->name, dev_name(dev->parent),218 dev_name(dev));219 else220 snprintf(snd->card->longname, sizeof(snd->card->longname),221 VIRTIO_SND_CARD_NAME " at %s/%s",222 dev_name(dev->parent), dev_name(dev));223 224 rc = virtsnd_jack_parse_cfg(snd);225 if (rc)226 return rc;227 228 rc = virtsnd_pcm_parse_cfg(snd);229 if (rc)230 return rc;231 232 rc = virtsnd_chmap_parse_cfg(snd);233 if (rc)234 return rc;235 236 if (virtio_has_feature(vdev, VIRTIO_SND_F_CTLS)) {237 rc = virtsnd_kctl_parse_cfg(snd);238 if (rc)239 return rc;240 }241 242 if (snd->njacks) {243 rc = virtsnd_jack_build_devs(snd);244 if (rc)245 return rc;246 }247 248 if (snd->nsubstreams) {249 rc = virtsnd_pcm_build_devs(snd);250 if (rc)251 return rc;252 }253 254 if (snd->nchmaps) {255 rc = virtsnd_chmap_build_devs(snd);256 if (rc)257 return rc;258 }259 260 if (snd->nkctls) {261 rc = virtsnd_kctl_build_devs(snd);262 if (rc)263 return rc;264 }265 266 return snd_card_register(snd->card);267}268 269/**270 * virtsnd_validate() - Validate if the device can be started.271 * @vdev: VirtIO parent device.272 *273 * Context: Any context.274 * Return: 0 on success, -EINVAL on failure.275 */276static int virtsnd_validate(struct virtio_device *vdev)277{278 if (!vdev->config->get) {279 dev_err(&vdev->dev, "configuration access disabled\n");280 return -EINVAL;281 }282 283 if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {284 dev_err(&vdev->dev,285 "device does not comply with spec version 1.x\n");286 return -EINVAL;287 }288 289 if (!virtsnd_msg_timeout_ms) {290 dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");291 return -EINVAL;292 }293 294 if (virtsnd_pcm_validate(vdev))295 return -EINVAL;296 297 return 0;298}299 300/**301 * virtsnd_probe() - Create and initialize the device.302 * @vdev: VirtIO parent device.303 *304 * Context: Any context that permits to sleep.305 * Return: 0 on success, -errno on failure.306 */307static int virtsnd_probe(struct virtio_device *vdev)308{309 struct virtio_snd *snd;310 unsigned int i;311 int rc;312 313 snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);314 if (!snd)315 return -ENOMEM;316 317 snd->vdev = vdev;318 INIT_LIST_HEAD(&snd->ctl_msgs);319 INIT_LIST_HEAD(&snd->pcm_list);320 321 vdev->priv = snd;322 323 for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)324 spin_lock_init(&snd->queues[i].lock);325 326 rc = virtsnd_find_vqs(snd);327 if (rc)328 goto on_exit;329 330 virtio_device_ready(vdev);331 332 rc = virtsnd_build_devs(snd);333 if (rc)334 goto on_exit;335 336 virtsnd_enable_event_vq(snd);337 338on_exit:339 if (rc)340 virtsnd_remove(vdev);341 342 return rc;343}344 345/**346 * virtsnd_remove() - Remove VirtIO and ALSA devices.347 * @vdev: VirtIO parent device.348 *349 * Context: Any context that permits to sleep.350 */351static void virtsnd_remove(struct virtio_device *vdev)352{353 struct virtio_snd *snd = vdev->priv;354 unsigned int i;355 356 virtsnd_disable_event_vq(snd);357 virtsnd_ctl_msg_cancel_all(snd);358 359 if (snd->card)360 snd_card_free(snd->card);361 362 vdev->config->del_vqs(vdev);363 virtio_reset_device(vdev);364 365 for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {366 struct virtio_pcm_substream *vss = &snd->substreams[i];367 368 cancel_work_sync(&vss->elapsed_period);369 virtsnd_pcm_msg_free(vss);370 }371 372 kfree(snd->event_msgs);373}374 375#ifdef CONFIG_PM_SLEEP376/**377 * virtsnd_freeze() - Suspend device.378 * @vdev: VirtIO parent device.379 *380 * Context: Any context.381 * Return: 0 on success, -errno on failure.382 */383static int virtsnd_freeze(struct virtio_device *vdev)384{385 struct virtio_snd *snd = vdev->priv;386 unsigned int i;387 388 virtsnd_disable_event_vq(snd);389 virtsnd_ctl_msg_cancel_all(snd);390 391 vdev->config->del_vqs(vdev);392 virtio_reset_device(vdev);393 394 for (i = 0; i < snd->nsubstreams; ++i)395 cancel_work_sync(&snd->substreams[i].elapsed_period);396 397 kfree(snd->event_msgs);398 snd->event_msgs = NULL;399 400 return 0;401}402 403/**404 * virtsnd_restore() - Resume device.405 * @vdev: VirtIO parent device.406 *407 * Context: Any context.408 * Return: 0 on success, -errno on failure.409 */410static int virtsnd_restore(struct virtio_device *vdev)411{412 struct virtio_snd *snd = vdev->priv;413 int rc;414 415 rc = virtsnd_find_vqs(snd);416 if (rc)417 return rc;418 419 virtio_device_ready(vdev);420 421 virtsnd_enable_event_vq(snd);422 423 return 0;424}425#endif /* CONFIG_PM_SLEEP */426 427static const struct virtio_device_id id_table[] = {428 { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },429 { 0 },430};431 432static unsigned int features[] = {433 VIRTIO_SND_F_CTLS434};435 436static struct virtio_driver virtsnd_driver = {437 .driver.name = KBUILD_MODNAME,438 .id_table = id_table,439 .feature_table = features,440 .feature_table_size = ARRAY_SIZE(features),441 .validate = virtsnd_validate,442 .probe = virtsnd_probe,443 .remove = virtsnd_remove,444#ifdef CONFIG_PM_SLEEP445 .freeze = virtsnd_freeze,446 .restore = virtsnd_restore,447#endif448};449 450module_virtio_driver(virtsnd_driver);451 452MODULE_DEVICE_TABLE(virtio, id_table);453MODULE_DESCRIPTION("Virtio sound card driver");454MODULE_LICENSE("GPL");455