Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/virtio/virtio_card.c
26378 views
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
u32 virtsnd_msg_timeout_ms = MSEC_PER_SEC;
15
module_param_named(msg_timeout_ms, virtsnd_msg_timeout_ms, uint, 0644);
16
MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds");
17
18
static 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
*/
29
static 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
*/
55
static 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
*/
82
static 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
*/
110
static 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
*/
160
static 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
*/
174
static 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
*/
197
static 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
*/
276
static 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
*/
307
static 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
338
on_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
*/
351
static 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
*/
383
static 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
*/
410
static 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
427
static const struct virtio_device_id id_table[] = {
428
{ VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
429
{ 0 },
430
};
431
432
static unsigned int features[] = {
433
VIRTIO_SND_F_CTLS
434
};
435
436
static 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
450
module_virtio_driver(virtsnd_driver);
451
452
MODULE_DEVICE_TABLE(virtio, id_table);
453
MODULE_DESCRIPTION("Virtio sound card driver");
454
MODULE_LICENSE("GPL");
455
456