Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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	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 the
 78 * 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	else
220		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_SLEEP
376/**
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_CTLS
434};
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_SLEEP
445	.freeze = virtsnd_freeze,
446	.restore = virtsnd_restore,
447#endif
448};
449
450module_virtio_driver(virtsnd_driver);
451
452MODULE_DEVICE_TABLE(virtio, id_table);
453MODULE_DESCRIPTION("Virtio sound card driver");
454MODULE_LICENSE("GPL");