Skip to content

Commit c825519

Browse files
zhuyingjiangNanlinXie
authored andcommitted
audio mediator device model
The device model is a userspace application on SOS to config the PCI devices for the UOS. Audio mediator device model is to config the virtual audio PCI device. Signed-off-by: Zhu Yingjiang <yingjiang.zhu@linux.intel.com>
1 parent 359e5cf commit c825519

File tree

1 file changed

+385
-0
lines changed

1 file changed

+385
-0
lines changed
Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
/*
2+
* Copyright (C) 2018 Intel Corporation. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*
6+
*/
7+
8+
/*
9+
* virtio audio
10+
* audio mediator device model
11+
*/
12+
13+
#include <err.h>
14+
#include <errno.h>
15+
16+
#include <fcntl.h>
17+
#include <stdio.h>
18+
#include <stdlib.h>
19+
#include <string.h>
20+
#include <unistd.h>
21+
#include <assert.h>
22+
#include <pthread.h>
23+
#include <sysexits.h>
24+
25+
#include "dm.h"
26+
#include "pci_core.h"
27+
#include "virtio.h"
28+
#include "virtio_kernel.h"
29+
#include "vmmapi.h" /* for vmctx */
30+
31+
/*
32+
* Size of queue was chosen experimentaly in a way
33+
* that it allows to do audio capture/playback without
34+
* any delay for interrupt/msg send, this value should
35+
* be tuned.
36+
*/
37+
#define VIRTIO_AUDIO_RINGSZ 1024
38+
39+
/*
40+
* Queue definitions.
41+
* Audio mediator uses two queues: one for interrupt and the other for messages.
42+
*/
43+
#define VIRTIO_AUDIO_VQ_NUM 2
44+
45+
const char *vbs_k_audio_dev_path = "/dev/vbs_k_audio";
46+
47+
static int virtio_audio_debug = 1;
48+
#define DPRINTF(params) do { if (virtio_audio_debug) printf params; } while (0)
49+
#define WPRINTF(params) (printf params)
50+
51+
struct virtio_audio {
52+
struct virtio_base base;
53+
struct virtio_vq_info vq[VIRTIO_AUDIO_VQ_NUM];
54+
pthread_mutex_t mtx;
55+
/* VBS-K variables */
56+
struct {
57+
enum VBS_K_STATUS kstatus;
58+
int audio_fd;
59+
struct vbs_dev_info kdev;
60+
struct vbs_vqs_info kvqs;
61+
} vbs_k;
62+
};
63+
64+
static int virtio_audio_kernel_start(struct virtio_audio *virt_audio);
65+
static int virtio_audio_kernel_stop(struct virtio_audio *virt_audio);
66+
static int virtio_audio_kernel_reset(struct virtio_audio *virt_audio);
67+
static int virtio_audio_kernel_dev_set(struct vbs_dev_info *kdev,
68+
const char *name, int vmid, int nvq,
69+
uint32_t feature, uint64_t pio_start,
70+
uint64_t pio_len);
71+
static int virtio_audio_kernel_vq_set(struct vbs_vqs_info *kvqs,
72+
unsigned int nvq, unsigned int idx,
73+
uint16_t qsize, uint32_t pfn,
74+
uint16_t msix_idx, uint64_t msix_addr,
75+
uint32_t msix_data);
76+
77+
static void virtio_audio_k_no_notify(void *base, struct virtio_vq_info *vq);
78+
static void virtio_audio_k_set_status(void *base, uint64_t status);
79+
static void virtio_audio_reset(void *base);
80+
81+
static struct virtio_ops virtio_audio_ops_k = {
82+
"virtio_audio", /* our name */
83+
VIRTIO_AUDIO_VQ_NUM, /* we support 2 virtqueue */
84+
0, /* config reg size */
85+
virtio_audio_reset, /* reset */
86+
virtio_audio_k_no_notify, /* device-wide qnotify */
87+
NULL, /* read virtio config */
88+
NULL, /* write virtio config */
89+
NULL, /* apply negotiated features */
90+
virtio_audio_k_set_status,/* called on guest set status */
91+
0, /* our capabilities */
92+
};
93+
94+
static int
95+
virtio_audio_kernel_init(struct virtio_audio *virt_audio)
96+
{
97+
if (virt_audio->vbs_k.audio_fd != -1) {
98+
WPRINTF(("virtio_audio: Ooops! Re-entered!!\n"));
99+
return -VIRTIO_ERROR_REENTER;
100+
}
101+
102+
virt_audio->vbs_k.audio_fd = open(vbs_k_audio_dev_path, O_RDWR);
103+
if (virt_audio->vbs_k.audio_fd < 0) {
104+
WPRINTF(("virtio_audio: Failed to open %s!\n",
105+
vbs_k_audio_dev_path));
106+
return -VIRTIO_ERROR_FD_OPEN_FAILED;
107+
}
108+
DPRINTF(("virtio_audio: Open %s success!\n",
109+
vbs_k_audio_dev_path));
110+
111+
memset(&virt_audio->vbs_k.kdev, 0, sizeof(struct vbs_dev_info));
112+
memset(&virt_audio->vbs_k.kvqs, 0, sizeof(struct vbs_vqs_info));
113+
114+
return VIRTIO_SUCCESS;
115+
}
116+
117+
static int
118+
virtio_audio_kernel_dev_set(struct vbs_dev_info *kdev, const char *name,
119+
int vmid, int nvq, uint32_t feature,
120+
uint64_t pio_start,
121+
uint64_t pio_len)
122+
{
123+
/* init kdev */
124+
strncpy(kdev->name, name, VBS_NAME_LEN);
125+
kdev->vmid = vmid;
126+
kdev->nvq = nvq;
127+
kdev->negotiated_features = feature;
128+
kdev->pio_range_start = pio_start;
129+
kdev->pio_range_len = pio_len;
130+
131+
return VIRTIO_SUCCESS;
132+
}
133+
134+
static int
135+
virtio_audio_kernel_vq_set(struct vbs_vqs_info *kvqs, unsigned int nvq,
136+
unsigned int idx, uint16_t qsize,
137+
uint32_t pfn, uint16_t msix_idx, uint64_t msix_addr,
138+
uint32_t msix_data)
139+
{
140+
if (nvq <= idx) {
141+
WPRINTF(("virtio_audio: wrong idx for vq_set!\n"));
142+
return -VIRTIO_ERROR_GENERAL;
143+
}
144+
145+
/* init kvqs */
146+
kvqs->nvq = nvq;
147+
kvqs->vqs[idx].qsize = qsize;
148+
kvqs->vqs[idx].pfn = pfn;
149+
kvqs->vqs[idx].msix_idx = msix_idx;
150+
kvqs->vqs[idx].msix_addr = msix_addr;
151+
kvqs->vqs[idx].msix_data = msix_data;
152+
153+
return VIRTIO_SUCCESS;
154+
}
155+
156+
static int
157+
virtio_audio_kernel_start(struct virtio_audio *virt_audio)
158+
{
159+
if (vbs_kernel_start(virt_audio->vbs_k.audio_fd,
160+
&virt_audio->vbs_k.kdev,
161+
&virt_audio->vbs_k.kvqs) < 0) {
162+
WPRINTF(("virtio_audio: Failed in vbs_k_start!\n"));
163+
return -VIRTIO_ERROR_START;
164+
}
165+
166+
DPRINTF(("virtio_audio: vbs_k_started!\n"));
167+
return VIRTIO_SUCCESS;
168+
}
169+
170+
static int
171+
virtio_audio_kernel_stop(struct virtio_audio *virt_audio)
172+
{
173+
return vbs_kernel_stop(virt_audio->vbs_k.audio_fd);
174+
}
175+
176+
static int
177+
virtio_audio_kernel_reset(struct virtio_audio *virt_audio)
178+
{
179+
memset(&virt_audio->vbs_k.kdev, 0, sizeof(struct vbs_dev_info));
180+
memset(&virt_audio->vbs_k.kvqs, 0, sizeof(struct vbs_vqs_info));
181+
182+
return vbs_kernel_reset(virt_audio->vbs_k.audio_fd);
183+
}
184+
185+
static void
186+
virtio_audio_reset(void *base)
187+
{
188+
struct virtio_audio *virt_audio;
189+
190+
virt_audio = (struct virtio_audio *)base;
191+
192+
DPRINTF(("virtio_audio: device reset requested !\n"));
193+
virtio_reset_dev(&virt_audio->base);
194+
DPRINTF(("virtio_audio: kstatus %d\n", virt_audio->vbs_k.kstatus));
195+
if (virt_audio->vbs_k.kstatus == VIRTIO_DEV_STARTED) {
196+
DPRINTF(("virtio_audio: VBS-K reset requested!\n"));
197+
virtio_audio_kernel_stop(virt_audio);
198+
virtio_audio_kernel_reset(virt_audio);
199+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_INITIAL;
200+
}
201+
}
202+
203+
/* VBS-K interface function implementations */
204+
static void
205+
virtio_audio_k_no_notify(void *base, struct virtio_vq_info *vq)
206+
{
207+
WPRINTF(("virtio_audio: VBS-K mode! Should not reach here!!\n"));
208+
}
209+
210+
/*
211+
* This callback gives us a chance to determine the timings
212+
* to kickoff VBS-K initialization
213+
*/
214+
static void
215+
virtio_audio_k_set_status(void *base, uint64_t status)
216+
{
217+
struct virtio_audio *virt_audio;
218+
int nvq;
219+
struct msix_table_entry *mte;
220+
uint64_t msix_addr = 0;
221+
uint32_t msix_data = 0;
222+
int rc, i, j;
223+
224+
virt_audio = (struct virtio_audio *)base;
225+
nvq = virt_audio->base.vops->nvq;
226+
227+
if (virt_audio->vbs_k.kstatus == VIRTIO_DEV_INIT_SUCCESS &&
228+
(status & VIRTIO_CR_STATUS_DRIVER_OK)) {
229+
/* time to kickoff VBS-K side */
230+
/* init vdev first */
231+
rc = virtio_audio_kernel_dev_set(
232+
&virt_audio->vbs_k.kdev,
233+
virt_audio->base.vops->name,
234+
virt_audio->base.dev->vmctx->vmid,
235+
nvq,
236+
virt_audio->base.negotiated_caps,
237+
/* currently we let VBS-K handle
238+
* kick register
239+
*
240+
* FIXME: the size should be returned
241+
* by a api in vhost.
242+
*/
243+
virt_audio->base.dev->bar[0].addr + 16,
244+
2);
245+
246+
for (i = 0; i < nvq; i++) {
247+
if (virt_audio->vq[i].msix_idx
248+
!= VIRTIO_MSI_NO_VECTOR) {
249+
j = virt_audio->vq[i].msix_idx;
250+
mte = &virt_audio->base.dev->msix.table[j];
251+
msix_addr = mte->addr;
252+
msix_data = mte->msg_data;
253+
}
254+
rc = virtio_audio_kernel_vq_set(
255+
&virt_audio->vbs_k.kvqs,
256+
nvq, i,
257+
virt_audio->vq[i].qsize,
258+
virt_audio->vq[i].pfn,
259+
virt_audio->vq[i].msix_idx,
260+
msix_addr,
261+
msix_data);
262+
263+
if (rc < 0) {
264+
WPRINTF(("audio: kernel_set_vq failed, "
265+
"i %d ret %d\n", i, rc));
266+
return;
267+
}
268+
}
269+
rc = virtio_audio_kernel_start(virt_audio);
270+
if (rc < 0) {
271+
WPRINTF(("virtio_audio: kernel_start() failed\n"));
272+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_START_FAILED;
273+
} else {
274+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_STARTED;
275+
}
276+
}
277+
}
278+
279+
static int
280+
virtio_audio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
281+
{
282+
struct virtio_audio *virt_audio;
283+
284+
pthread_mutexattr_t attr;
285+
int rc;
286+
287+
virt_audio = calloc(1, sizeof(struct virtio_audio));
288+
if (!virt_audio) {
289+
WPRINTF(("virtio_audio: calloc returns NULL\n"));
290+
return -1;
291+
}
292+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_INITIAL;
293+
virt_audio->vbs_k.audio_fd = -1;
294+
295+
/* init mutex attribute properly */
296+
rc = pthread_mutexattr_init(&attr);
297+
if (rc)
298+
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
299+
300+
if (virtio_uses_msix()) {
301+
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
302+
if (rc)
303+
DPRINTF(("virtio_msix: mutexattr_settype "
304+
"failed with error %d!\n", rc));
305+
} else {
306+
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
307+
if (rc)
308+
DPRINTF(("virtio_intx: mutexattr_settype "
309+
"failed with error %d!\n", rc));
310+
}
311+
312+
rc = pthread_mutex_init(&virt_audio->mtx, &attr);
313+
if (rc)
314+
DPRINTF(("mutex init failed with error %d!\n", rc));
315+
316+
virtio_linkup(&virt_audio->base,
317+
&virtio_audio_ops_k,
318+
virt_audio,
319+
dev,
320+
virt_audio->vq);
321+
322+
rc = virtio_audio_kernel_init(virt_audio);
323+
if (rc < 0) {
324+
WPRINTF(("virtio_audio: VBS-K init failed,error %d!\n", rc));
325+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_INIT_FAILED;
326+
free(virt_audio);
327+
return -1;
328+
}
329+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_INIT_SUCCESS;
330+
virt_audio->base.mtx = &virt_audio->mtx;
331+
332+
/* vq[0] and vq[1] are for interrupt and messages */
333+
virt_audio->vq[0].qsize = VIRTIO_AUDIO_RINGSZ;
334+
virt_audio->vq[1].qsize = VIRTIO_AUDIO_RINGSZ;
335+
336+
/* initialize config space */
337+
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_AUDIO);
338+
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
339+
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_MULTIMEDIA);
340+
pci_set_cfgdata8(dev, PCIR_SUBCLASS, PCIS_MULTIMEDIA_AUDIO);
341+
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_AUDIO);
342+
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
343+
344+
if (virtio_interrupt_init(&virt_audio->base, virtio_uses_msix())) {
345+
free(virt_audio);
346+
return -1;
347+
}
348+
virtio_set_io_bar(&virt_audio->base, 0);
349+
350+
return 0;
351+
}
352+
353+
static void
354+
virtio_audio_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
355+
{
356+
struct virtio_audio *virt_audio;
357+
358+
virt_audio = dev->arg;
359+
if (!virt_audio) {
360+
DPRINTF(("%s: virtio_audio is NULL!\n", __func__));
361+
return;
362+
}
363+
if (virt_audio->vbs_k.kstatus == VIRTIO_DEV_STARTED) {
364+
DPRINTF(("%s: deinit virtio_audio_k!\n", __func__));
365+
virtio_audio_kernel_stop(virt_audio);
366+
virtio_audio_kernel_reset(virt_audio);
367+
virt_audio->vbs_k.kstatus = VIRTIO_DEV_INITIAL;
368+
assert(virt_audio->vbs_k.audio_fd >= 0);
369+
close(virt_audio->vbs_k.audio_fd);
370+
virt_audio->vbs_k.audio_fd = -1;
371+
}
372+
pthread_mutex_destroy(&virt_audio->mtx);
373+
DPRINTF(("%s: free struct virtio_audio!\n", __func__));
374+
free((struct virtio_audio *)dev->arg);
375+
}
376+
377+
struct pci_vdev_ops pci_ops_virtio_audio = {
378+
.class_name = "virtio-audio",
379+
.vdev_init = virtio_audio_init,
380+
.vdev_deinit = virtio_audio_deinit,
381+
.vdev_barwrite = virtio_pci_write,
382+
.vdev_barread = virtio_pci_read
383+
};
384+
385+
DEFINE_PCI_DEVTYPE(pci_ops_virtio_audio);

0 commit comments

Comments
 (0)