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 14 static void virtsnd_remove(struct virtio_device *vdev); 15 16 /** 17 * virtsnd_event_send() - Add an event to the event queue. 18 * @vqueue: Underlying event virtqueue. 19 * @event: Event. 20 * @notify: Indicates whether or not to send a notification to the device. 21 * @gfp: Kernel flags for memory allocation. 22 * 23 * Context: Any context. 24 */ 25 static void virtsnd_event_send(struct virtqueue *vqueue, 26 struct virtio_snd_event *event, bool notify, 27 gfp_t gfp) 28 { 29 struct scatterlist sg; 30 struct scatterlist *psgs[1] = { &sg }; 31 32 /* reset event content */ 33 memset(event, 0, sizeof(*event)); 34 35 sg_init_one(&sg, event, sizeof(*event)); 36 37 if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify) 38 return; 39 40 if (virtqueue_kick_prepare(vqueue)) 41 virtqueue_notify(vqueue); 42 } 43 44 /** 45 * virtsnd_event_dispatch() - Dispatch an event from the device side. 46 * @snd: VirtIO sound device. 47 * @event: VirtIO sound event. 48 * 49 * Context: Any context. 50 */ 51 static void virtsnd_event_dispatch(struct virtio_snd *snd, 52 struct virtio_snd_event *event) 53 { 54 } 55 56 /** 57 * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. 58 * @vqueue: Underlying event virtqueue. 59 * 60 * This callback function is called upon a vring interrupt request from the 61 * device. 62 * 63 * Context: Interrupt context. 64 */ 65 static void virtsnd_event_notify_cb(struct virtqueue *vqueue) 66 { 67 struct virtio_snd *snd = vqueue->vdev->priv; 68 struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 69 struct virtio_snd_event *event; 70 u32 length; 71 unsigned long flags; 72 73 spin_lock_irqsave(&queue->lock, flags); 74 do { 75 virtqueue_disable_cb(vqueue); 76 while ((event = virtqueue_get_buf(vqueue, &length))) { 77 virtsnd_event_dispatch(snd, event); 78 virtsnd_event_send(vqueue, event, true, GFP_ATOMIC); 79 } 80 if (unlikely(virtqueue_is_broken(vqueue))) 81 break; 82 } while (!virtqueue_enable_cb(vqueue)); 83 spin_unlock_irqrestore(&queue->lock, flags); 84 } 85 86 /** 87 * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. 88 * @snd: VirtIO sound device. 89 * 90 * After calling this function, the event queue is disabled. 91 * 92 * Context: Any context. 93 * Return: 0 on success, -errno on failure. 94 */ 95 static int virtsnd_find_vqs(struct virtio_snd *snd) 96 { 97 struct virtio_device *vdev = snd->vdev; 98 static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { 99 [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb 100 }; 101 static const char *names[VIRTIO_SND_VQ_MAX] = { 102 [VIRTIO_SND_VQ_EVENT] = "virtsnd-event" 103 }; 104 struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; 105 unsigned int i; 106 unsigned int n; 107 int rc; 108 109 rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, 110 NULL); 111 if (rc) { 112 dev_err(&vdev->dev, "failed to initialize virtqueues\n"); 113 return rc; 114 } 115 116 for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) 117 snd->queues[i].vqueue = vqs[i]; 118 119 /* Allocate events and populate the event queue */ 120 virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); 121 122 n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); 123 124 snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs), 125 GFP_KERNEL); 126 if (!snd->event_msgs) 127 return -ENOMEM; 128 129 for (i = 0; i < n; ++i) 130 virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], 131 &snd->event_msgs[i], false, GFP_KERNEL); 132 133 return 0; 134 } 135 136 /** 137 * virtsnd_enable_event_vq() - Enable the event virtqueue. 138 * @snd: VirtIO sound device. 139 * 140 * Context: Any context. 141 */ 142 static void virtsnd_enable_event_vq(struct virtio_snd *snd) 143 { 144 struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 145 146 if (!virtqueue_enable_cb(queue->vqueue)) 147 virtsnd_event_notify_cb(queue->vqueue); 148 } 149 150 /** 151 * virtsnd_disable_event_vq() - Disable the event virtqueue. 152 * @snd: VirtIO sound device. 153 * 154 * Context: Any context. 155 */ 156 static void virtsnd_disable_event_vq(struct virtio_snd *snd) 157 { 158 struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 159 struct virtio_snd_event *event; 160 u32 length; 161 unsigned long flags; 162 163 if (queue->vqueue) { 164 spin_lock_irqsave(&queue->lock, flags); 165 virtqueue_disable_cb(queue->vqueue); 166 while ((event = virtqueue_get_buf(queue->vqueue, &length))) 167 virtsnd_event_dispatch(snd, event); 168 spin_unlock_irqrestore(&queue->lock, flags); 169 } 170 } 171 172 /** 173 * virtsnd_build_devs() - Read configuration and build ALSA devices. 174 * @snd: VirtIO sound device. 175 * 176 * Context: Any context that permits to sleep. 177 * Return: 0 on success, -errno on failure. 178 */ 179 static int virtsnd_build_devs(struct virtio_snd *snd) 180 { 181 struct virtio_device *vdev = snd->vdev; 182 struct device *dev = &vdev->dev; 183 int rc; 184 185 rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 186 THIS_MODULE, 0, &snd->card); 187 if (rc < 0) 188 return rc; 189 190 snd->card->private_data = snd; 191 192 strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER, 193 sizeof(snd->card->driver)); 194 strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME, 195 sizeof(snd->card->shortname)); 196 if (dev->parent->bus) 197 snprintf(snd->card->longname, sizeof(snd->card->longname), 198 VIRTIO_SND_CARD_NAME " at %s/%s/%s", 199 dev->parent->bus->name, dev_name(dev->parent), 200 dev_name(dev)); 201 else 202 snprintf(snd->card->longname, sizeof(snd->card->longname), 203 VIRTIO_SND_CARD_NAME " at %s/%s", 204 dev_name(dev->parent), dev_name(dev)); 205 206 return snd_card_register(snd->card); 207 } 208 209 /** 210 * virtsnd_validate() - Validate if the device can be started. 211 * @vdev: VirtIO parent device. 212 * 213 * Context: Any context. 214 * Return: 0 on success, -EINVAL on failure. 215 */ 216 static int virtsnd_validate(struct virtio_device *vdev) 217 { 218 if (!vdev->config->get) { 219 dev_err(&vdev->dev, "configuration access disabled\n"); 220 return -EINVAL; 221 } 222 223 if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { 224 dev_err(&vdev->dev, 225 "device does not comply with spec version 1.x\n"); 226 return -EINVAL; 227 } 228 229 return 0; 230 } 231 232 /** 233 * virtsnd_probe() - Create and initialize the device. 234 * @vdev: VirtIO parent device. 235 * 236 * Context: Any context that permits to sleep. 237 * Return: 0 on success, -errno on failure. 238 */ 239 static int virtsnd_probe(struct virtio_device *vdev) 240 { 241 struct virtio_snd *snd; 242 unsigned int i; 243 int rc; 244 245 snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); 246 if (!snd) 247 return -ENOMEM; 248 249 snd->vdev = vdev; 250 251 vdev->priv = snd; 252 253 for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) 254 spin_lock_init(&snd->queues[i].lock); 255 256 rc = virtsnd_find_vqs(snd); 257 if (rc) 258 goto on_exit; 259 260 virtio_device_ready(vdev); 261 262 rc = virtsnd_build_devs(snd); 263 if (rc) 264 goto on_exit; 265 266 virtsnd_enable_event_vq(snd); 267 268 on_exit: 269 if (rc) 270 virtsnd_remove(vdev); 271 272 return rc; 273 } 274 275 /** 276 * virtsnd_remove() - Remove VirtIO and ALSA devices. 277 * @vdev: VirtIO parent device. 278 * 279 * Context: Any context that permits to sleep. 280 */ 281 static void virtsnd_remove(struct virtio_device *vdev) 282 { 283 struct virtio_snd *snd = vdev->priv; 284 285 virtsnd_disable_event_vq(snd); 286 287 if (snd->card) 288 snd_card_free(snd->card); 289 290 vdev->config->del_vqs(vdev); 291 vdev->config->reset(vdev); 292 293 kfree(snd->event_msgs); 294 } 295 296 static const struct virtio_device_id id_table[] = { 297 { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, 298 { 0 }, 299 }; 300 301 static struct virtio_driver virtsnd_driver = { 302 .driver.name = KBUILD_MODNAME, 303 .driver.owner = THIS_MODULE, 304 .id_table = id_table, 305 .validate = virtsnd_validate, 306 .probe = virtsnd_probe, 307 .remove = virtsnd_remove, 308 }; 309 310 static int __init init(void) 311 { 312 return register_virtio_driver(&virtsnd_driver); 313 } 314 module_init(init); 315 316 static void __exit fini(void) 317 { 318 unregister_virtio_driver(&virtsnd_driver); 319 } 320 module_exit(fini); 321 322 MODULE_DEVICE_TABLE(virtio, id_table); 323 MODULE_DESCRIPTION("Virtio sound card driver"); 324 MODULE_LICENSE("GPL"); 325