Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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");