linux/sound/virtio/virtio_card.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * virtio-snd: Virtio sound device
   4 * Copyright (C) 2021 OpenSynergy GmbH
   5 */
   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        }
  68}
  69
  70/**
  71 * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
  72 * @vqueue: Underlying event virtqueue.
  73 *
  74 * This callback function is called upon a vring interrupt request from the
  75 * device.
  76 *
  77 * Context: Interrupt context.
  78 */
  79static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
  80{
  81        struct virtio_snd *snd = vqueue->vdev->priv;
  82        struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
  83        struct virtio_snd_event *event;
  84        u32 length;
  85        unsigned long flags;
  86
  87        spin_lock_irqsave(&queue->lock, flags);
  88        do {
  89                virtqueue_disable_cb(vqueue);
  90                while ((event = virtqueue_get_buf(vqueue, &length))) {
  91                        virtsnd_event_dispatch(snd, event);
  92                        virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
  93                }
  94                if (unlikely(virtqueue_is_broken(vqueue)))
  95                        break;
  96        } while (!virtqueue_enable_cb(vqueue));
  97        spin_unlock_irqrestore(&queue->lock, flags);
  98}
  99
 100/**
 101 * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
 102 * @snd: VirtIO sound device.
 103 *
 104 * After calling this function, the event queue is disabled.
 105 *
 106 * Context: Any context.
 107 * Return: 0 on success, -errno on failure.
 108 */
 109static int virtsnd_find_vqs(struct virtio_snd *snd)
 110{
 111        struct virtio_device *vdev = snd->vdev;
 112        static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
 113                [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
 114                [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
 115                [VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
 116                [VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
 117        };
 118        static const char *names[VIRTIO_SND_VQ_MAX] = {
 119                [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
 120                [VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
 121                [VIRTIO_SND_VQ_TX] = "virtsnd-tx",
 122                [VIRTIO_SND_VQ_RX] = "virtsnd-rx"
 123        };
 124        struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
 125        unsigned int i;
 126        unsigned int n;
 127        int rc;
 128
 129        rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
 130                             NULL);
 131        if (rc) {
 132                dev_err(&vdev->dev, "failed to initialize virtqueues\n");
 133                return rc;
 134        }
 135
 136        for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
 137                snd->queues[i].vqueue = vqs[i];
 138
 139        /* Allocate events and populate the event queue */
 140        virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
 141
 142        n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
 143
 144        snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
 145                                        GFP_KERNEL);
 146        if (!snd->event_msgs)
 147                return -ENOMEM;
 148
 149        for (i = 0; i < n; ++i)
 150                virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
 151                                   &snd->event_msgs[i], false, GFP_KERNEL);
 152
 153        return 0;
 154}
 155
 156/**
 157 * virtsnd_enable_event_vq() - Enable the event virtqueue.
 158 * @snd: VirtIO sound device.
 159 *
 160 * Context: Any context.
 161 */
 162static void virtsnd_enable_event_vq(struct virtio_snd *snd)
 163{
 164        struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
 165
 166        if (!virtqueue_enable_cb(queue->vqueue))
 167                virtsnd_event_notify_cb(queue->vqueue);
 168}
 169
 170/**
 171 * virtsnd_disable_event_vq() - Disable the event virtqueue.
 172 * @snd: VirtIO sound device.
 173 *
 174 * Context: Any context.
 175 */
 176static void virtsnd_disable_event_vq(struct virtio_snd *snd)
 177{
 178        struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
 179        struct virtio_snd_event *event;
 180        u32 length;
 181        unsigned long flags;
 182
 183        if (queue->vqueue) {
 184                spin_lock_irqsave(&queue->lock, flags);
 185                virtqueue_disable_cb(queue->vqueue);
 186                while ((event = virtqueue_get_buf(queue->vqueue, &length)))
 187                        virtsnd_event_dispatch(snd, event);
 188                spin_unlock_irqrestore(&queue->lock, flags);
 189        }
 190}
 191
 192/**
 193 * virtsnd_build_devs() - Read configuration and build ALSA devices.
 194 * @snd: VirtIO sound device.
 195 *
 196 * Context: Any context that permits to sleep.
 197 * Return: 0 on success, -errno on failure.
 198 */
 199static int virtsnd_build_devs(struct virtio_snd *snd)
 200{
 201        struct virtio_device *vdev = snd->vdev;
 202        struct device *dev = &vdev->dev;
 203        int rc;
 204
 205        rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 206                          THIS_MODULE, 0, &snd->card);
 207        if (rc < 0)
 208                return rc;
 209
 210        snd->card->private_data = snd;
 211
 212        strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
 213                sizeof(snd->card->driver));
 214        strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
 215                sizeof(snd->card->shortname));
 216        if (dev->parent->bus)
 217                snprintf(snd->card->longname, sizeof(snd->card->longname),
 218                         VIRTIO_SND_CARD_NAME " at %s/%s/%s",
 219                         dev->parent->bus->name, dev_name(dev->parent),
 220                         dev_name(dev));
 221        else
 222                snprintf(snd->card->longname, sizeof(snd->card->longname),
 223                         VIRTIO_SND_CARD_NAME " at %s/%s",
 224                         dev_name(dev->parent), dev_name(dev));
 225
 226        rc = virtsnd_jack_parse_cfg(snd);
 227        if (rc)
 228                return rc;
 229
 230        rc = virtsnd_pcm_parse_cfg(snd);
 231        if (rc)
 232                return rc;
 233
 234        rc = virtsnd_chmap_parse_cfg(snd);
 235        if (rc)
 236                return rc;
 237
 238        if (snd->njacks) {
 239                rc = virtsnd_jack_build_devs(snd);
 240                if (rc)
 241                        return rc;
 242        }
 243
 244        if (snd->nsubstreams) {
 245                rc = virtsnd_pcm_build_devs(snd);
 246                if (rc)
 247                        return rc;
 248        }
 249
 250        if (snd->nchmaps) {
 251                rc = virtsnd_chmap_build_devs(snd);
 252                if (rc)
 253                        return rc;
 254        }
 255
 256        return snd_card_register(snd->card);
 257}
 258
 259/**
 260 * virtsnd_validate() - Validate if the device can be started.
 261 * @vdev: VirtIO parent device.
 262 *
 263 * Context: Any context.
 264 * Return: 0 on success, -EINVAL on failure.
 265 */
 266static int virtsnd_validate(struct virtio_device *vdev)
 267{
 268        if (!vdev->config->get) {
 269                dev_err(&vdev->dev, "configuration access disabled\n");
 270                return -EINVAL;
 271        }
 272
 273        if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
 274                dev_err(&vdev->dev,
 275                        "device does not comply with spec version 1.x\n");
 276                return -EINVAL;
 277        }
 278
 279        if (!virtsnd_msg_timeout_ms) {
 280                dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");
 281                return -EINVAL;
 282        }
 283
 284        if (virtsnd_pcm_validate(vdev))
 285                return -EINVAL;
 286
 287        return 0;
 288}
 289
 290/**
 291 * virtsnd_probe() - Create and initialize the device.
 292 * @vdev: VirtIO parent device.
 293 *
 294 * Context: Any context that permits to sleep.
 295 * Return: 0 on success, -errno on failure.
 296 */
 297static int virtsnd_probe(struct virtio_device *vdev)
 298{
 299        struct virtio_snd *snd;
 300        unsigned int i;
 301        int rc;
 302
 303        snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
 304        if (!snd)
 305                return -ENOMEM;
 306
 307        snd->vdev = vdev;
 308        INIT_LIST_HEAD(&snd->ctl_msgs);
 309        INIT_LIST_HEAD(&snd->pcm_list);
 310
 311        vdev->priv = snd;
 312
 313        for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
 314                spin_lock_init(&snd->queues[i].lock);
 315
 316        rc = virtsnd_find_vqs(snd);
 317        if (rc)
 318                goto on_exit;
 319
 320        virtio_device_ready(vdev);
 321
 322        rc = virtsnd_build_devs(snd);
 323        if (rc)
 324                goto on_exit;
 325
 326        virtsnd_enable_event_vq(snd);
 327
 328on_exit:
 329        if (rc)
 330                virtsnd_remove(vdev);
 331
 332        return rc;
 333}
 334
 335/**
 336 * virtsnd_remove() - Remove VirtIO and ALSA devices.
 337 * @vdev: VirtIO parent device.
 338 *
 339 * Context: Any context that permits to sleep.
 340 */
 341static void virtsnd_remove(struct virtio_device *vdev)
 342{
 343        struct virtio_snd *snd = vdev->priv;
 344        unsigned int i;
 345
 346        virtsnd_disable_event_vq(snd);
 347        virtsnd_ctl_msg_cancel_all(snd);
 348
 349        if (snd->card)
 350                snd_card_free(snd->card);
 351
 352        vdev->config->del_vqs(vdev);
 353        vdev->config->reset(vdev);
 354
 355        for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
 356                struct virtio_pcm_substream *vss = &snd->substreams[i];
 357
 358                cancel_work_sync(&vss->elapsed_period);
 359                virtsnd_pcm_msg_free(vss);
 360        }
 361
 362        kfree(snd->event_msgs);
 363}
 364
 365#ifdef CONFIG_PM_SLEEP
 366/**
 367 * virtsnd_freeze() - Suspend device.
 368 * @vdev: VirtIO parent device.
 369 *
 370 * Context: Any context.
 371 * Return: 0 on success, -errno on failure.
 372 */
 373static int virtsnd_freeze(struct virtio_device *vdev)
 374{
 375        struct virtio_snd *snd = vdev->priv;
 376        unsigned int i;
 377
 378        virtsnd_disable_event_vq(snd);
 379        virtsnd_ctl_msg_cancel_all(snd);
 380
 381        vdev->config->del_vqs(vdev);
 382        vdev->config->reset(vdev);
 383
 384        for (i = 0; i < snd->nsubstreams; ++i)
 385                cancel_work_sync(&snd->substreams[i].elapsed_period);
 386
 387        kfree(snd->event_msgs);
 388        snd->event_msgs = NULL;
 389
 390        return 0;
 391}
 392
 393/**
 394 * virtsnd_restore() - Resume device.
 395 * @vdev: VirtIO parent device.
 396 *
 397 * Context: Any context.
 398 * Return: 0 on success, -errno on failure.
 399 */
 400static int virtsnd_restore(struct virtio_device *vdev)
 401{
 402        struct virtio_snd *snd = vdev->priv;
 403        int rc;
 404
 405        rc = virtsnd_find_vqs(snd);
 406        if (rc)
 407                return rc;
 408
 409        virtio_device_ready(vdev);
 410
 411        virtsnd_enable_event_vq(snd);
 412
 413        return 0;
 414}
 415#endif /* CONFIG_PM_SLEEP */
 416
 417static const struct virtio_device_id id_table[] = {
 418        { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
 419        { 0 },
 420};
 421
 422static struct virtio_driver virtsnd_driver = {
 423        .driver.name = KBUILD_MODNAME,
 424        .driver.owner = THIS_MODULE,
 425        .id_table = id_table,
 426        .validate = virtsnd_validate,
 427        .probe = virtsnd_probe,
 428        .remove = virtsnd_remove,
 429#ifdef CONFIG_PM_SLEEP
 430        .freeze = virtsnd_freeze,
 431        .restore = virtsnd_restore,
 432#endif
 433};
 434
 435module_virtio_driver(virtsnd_driver);
 436
 437MODULE_DEVICE_TABLE(virtio, id_table);
 438MODULE_DESCRIPTION("Virtio sound card driver");
 439MODULE_LICENSE("GPL");
 440