Skip to content

Commit e4b1e34

Browse files
committed
Implement VirtIO sound device playback
The limitations are listed as follows: 1. The emulator will hang if PulseAudio is enabled on host. The reason is that the host OS cannot close CNFA driver because CNFA driver reference count is not zero (PulseAudio holds one reference count). What's worse, CNFA cannot initialize if PulseAudio is disabled, As it needs a dedicated threading model (CNFA uses different threading models between pure ALSA and PulseAudio environment), we suggest users need mitigations (for instance, restart the emulator after playing sound) or just wait for the future release. 2. The playback may play with repeating artifact (for example, A "front center" ALSA example sound will sound like "front front center"). The root cause is the Linux Kernel and it hasn't got fixed even in mainline version. See https://lore.kernel.org/all/[email protected]/T/ for more information.
1 parent 36fc1b2 commit e4b1e34

File tree

14 files changed

+1480
-4
lines changed

14 files changed

+1480
-4
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
- name: install-dependencies
1111
run: |
1212
sudo apt-get install build-essential device-tree-compiler expect
13+
sudo apt-get install libasound2-dev libudev-dev
1314
- name: default build
1415
run: make
1516
shell: bash

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "cnfa"]
2+
path = cnfa
3+
url = https://github.com/cntools/cnfa
4+
shallow = true

Makefile

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
include mk/common.mk
2+
include mk/check-libs.mk
23

34
CC ?= gcc
45
CFLAGS := -O2 -g -Wall -Wextra
@@ -13,6 +14,8 @@ OBJS_EXTRA :=
1314
# command line option
1415
OPTS :=
1516

17+
LDFLAGS := -lm
18+
1619
# virtio-blk
1720
ENABLE_VIRTIOBLK ?= 1
1821
$(call set-feature, VIRTIOBLK)
@@ -43,6 +46,51 @@ ifeq ($(call has, VIRTIONET), 1)
4346
OBJS_EXTRA += netdev.o
4447
endif
4548

49+
# virtio-snd
50+
ENABLE_VIRTIOSND ?= 1
51+
ifneq ($(UNAME_S),$(filter $(UNAME_S),Linux Darwin))
52+
ENABLE_VIRTIOSND := 0
53+
endif
54+
55+
# Check ALSA installation
56+
ifeq ($(UNAME_S),Linux)
57+
ifeq (0, $(call check-alsa))
58+
$(warning No libasound installed. Check libasound in advance.)
59+
ENABLE_VIRTIOSND := 0
60+
endif
61+
endif
62+
# Check core audio installation
63+
ifeq ($(UNAME_S),Darwin)
64+
ifeq (0, $(call check-coreaudio))
65+
$(warning No CoreAudio framework installed.)
66+
ENABLE_VIRTIOSND := 0
67+
endif
68+
endif
69+
$(call set-feature, VIRTIOSND)
70+
ifeq ($(call has, VIRTIOSND), 1)
71+
OBJS_EXTRA += virtio-snd.o
72+
73+
ifeq ($(UNAME_S),Linux)
74+
LDFLAGS += -lasound -lpthread
75+
else ifeq ($(UNAME_S),Darwin)
76+
LDFLAGS += -framework AudioToolbox -lpthread
77+
endif
78+
CFLAGS += -Icnfa
79+
80+
cnfa/Makefile:
81+
git submodule update --init cnfa
82+
cnfa/os_generic: cnfa/Makefile
83+
$(MAKE) -C $(dir $<) os_generic.h
84+
CNFA_LIB := cnfa/CNFA_sf.h
85+
$(CNFA_LIB): cnfa/Makefile cnfa/os_generic
86+
$(MAKE) -C $(dir $<) CNFA_sf.h
87+
main.o: $(CNFA_LIB)
88+
endif
89+
90+
# .DEFAULT_GOAL should be set to all since the very first target is not all
91+
# after git submodule.
92+
.DEFAULT_GOAL := all
93+
4694
BIN = semu
4795
all: $(BIN) minimal.dtb
4896

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ A minimalist RISC-V system emulator capable of running Linux the kernel and corr
99
- UART: 8250/16550
1010
- PLIC (platform-level interrupt controller): 32 interrupts, no priority
1111
- Standard SBI, with the timer extension
12-
- VirtIO: virtio-blk acquires disk image from the host, and virtio-net is mapped as TAP interface
12+
- Three types of I/O support using VirtIO standard:
13+
- virtio-blk acquires disk image from the host.
14+
- virtio-net is mapped as TAP interface.
15+
- virtio-snd uses ALSA for sound operation with the following limitations:
16+
- The emulator will hang if PulseAudio is enabled on host.
17+
- The playback may plays with repeating artifact.
1318

1419
## Prerequisites
1520

cnfa

Submodule cnfa added at 60bcddd

configs/buildroot.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ BR2_RELRO_NONE=y
3636
# BR2_RELRO_PARTIAL is not set
3737
# BR2_RELRO_FULL is not set
3838
BR2_FORTIFY_SOURCE_1=y
39+
BR2_PACKAGE_ALSA_UTILS=y
40+
BR2_PACKAGE_ALSA_UTILS_APLAY=y
41+
BR2_PACKAGE_ALSA_UTILS_SPEAKER_TEST=y
3942
# BR2_PACKAGE_URANDOM_SCRIPTS is not set
4043
BR2_TARGET_ROOTFS_CPIO=y
4144
BR2_TARGET_ROOTFS_CPIO_FULL=y

configs/linux.config

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ CONFIG_LOCALVERSION_AUTO=y
3030
CONFIG_BUILD_SALT=""
3131
CONFIG_DEFAULT_INIT=""
3232
CONFIG_DEFAULT_HOSTNAME="(none)"
33-
# CONFIG_SYSVIPC is not set
33+
CONFIG_SYSVIPC=y
34+
CONFIG_SYSVIPC_SYSCTL=y
3435
# CONFIG_POSIX_MQUEUE is not set
3536
# CONFIG_WATCH_QUEUE is not set
3637
# CONFIG_CROSS_MEMORY_ATTACH is not set
@@ -936,7 +937,9 @@ CONFIG_DUMMY_CONSOLE_ROWS=25
936937
# end of Console display driver support
937938
# end of Graphics support
938939

939-
# CONFIG_SOUND is not set
940+
CONFIG_SOUND=y
941+
CONFIG_SND=y
942+
CONFIG_SND_VIRTIO=y
940943

941944
#
942945
# HID support

device.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,53 @@ void aclint_sswi_write(hart_t *hart,
260260
uint8_t width,
261261
uint32_t value);
262262

263+
/* VirtIO-Sound */
264+
265+
#if SEMU_HAS(VIRTIOSND)
266+
#define IRQ_VSND 4
267+
#define IRQ_VSND_BIT (1 << IRQ_VSND)
268+
269+
typedef struct {
270+
uint32_t QueueNum;
271+
uint32_t QueueDesc;
272+
uint32_t QueueAvail;
273+
uint32_t QueueUsed;
274+
uint16_t last_avail;
275+
bool ready;
276+
} virtio_snd_queue_t;
277+
278+
typedef struct {
279+
/* feature negotiation */
280+
uint32_t DeviceFeaturesSel;
281+
uint32_t DriverFeatures;
282+
uint32_t DriverFeaturesSel;
283+
/* queue config */
284+
uint32_t QueueSel;
285+
virtio_snd_queue_t queues[4];
286+
/* status */
287+
uint32_t Status;
288+
uint32_t InterruptStatus;
289+
/* supplied by environment */
290+
uint32_t *ram;
291+
/* implementation-specific */
292+
void *priv;
293+
} virtio_snd_state_t;
294+
295+
void virtio_snd_read(hart_t *core,
296+
virtio_snd_state_t *vsnd,
297+
uint32_t addr,
298+
uint8_t width,
299+
uint32_t *value);
300+
301+
void virtio_snd_write(hart_t *core,
302+
virtio_snd_state_t *vsnd,
303+
uint32_t addr,
304+
uint8_t width,
305+
uint32_t value);
306+
307+
bool virtio_snd_init(virtio_snd_state_t *vsnd);
308+
#endif /* SEMU_HAS(VIRTIOSND) */
309+
263310
/* memory mapping */
264311
typedef struct {
265312
bool stopped;
@@ -277,4 +324,7 @@ typedef struct {
277324
mtimer_state_t mtimer;
278325
mswi_state_t mswi;
279326
sswi_state_t sswi;
327+
#if SEMU_HAS(VIRTIOSND)
328+
virtio_snd_state_t vsnd;
329+
#endif
280330
} emu_state_t;

feature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
#define SEMU_FEATURE_VIRTIONET 1
1313
#endif
1414

15+
/* virtio-snd */
16+
#ifndef SEMU_FEATURE_VIRTIOSND
17+
#define SEMU_FEATURE_VIRTIOSND 1
18+
#endif
19+
1520
/* Feature test macro */
1621
#define SEMU_HAS(x) SEMU_FEATURE_##x

main.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ static void emu_update_swi_interrupt(hart_t *hart)
8888
aclint_sswi_update_interrupts(hart, &data->sswi);
8989
}
9090

91+
#if SEMU_HAS(VIRTIOSND)
92+
static void emu_update_vsnd_interrupts(vm_t *vm)
93+
{
94+
emu_state_t *data = PRIV(vm->hart[0]);
95+
if (data->vsnd.InterruptStatus)
96+
data->plic.active |= IRQ_VSND_BIT;
97+
else
98+
data->plic.active &= ~IRQ_VSND_BIT;
99+
plic_update_interrupts(vm, &data->plic);
100+
}
101+
#endif
102+
91103
static void mem_load(hart_t *hart,
92104
uint32_t addr,
93105
uint8_t width,
@@ -137,6 +149,12 @@ static void mem_load(hart_t *hart,
137149
aclint_sswi_read(hart, &data->sswi, addr & 0xFFFFF, width, value);
138150
aclint_sswi_update_interrupts(hart, &data->sswi);
139151
return;
152+
#if SEMU_HAS(VIRTIOSND)
153+
case 0x46: /* virtio-snd */
154+
virtio_snd_read(hart, &data->vsnd, addr & 0xFFFFF, width, value);
155+
emu_update_vsnd_interrupts(hart->vm);
156+
return;
157+
#endif
140158
}
141159
}
142160
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
@@ -191,6 +209,12 @@ static void mem_store(hart_t *hart,
191209
aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value);
192210
aclint_sswi_update_interrupts(hart, &data->sswi);
193211
return;
212+
#if SEMU_HAS(VIRTIOSND)
213+
case 0x46: /* virtio-snd */
214+
virtio_snd_write(hart, &data->vsnd, addr & 0xFFFFF, width, value);
215+
emu_update_vsnd_interrupts(hart->vm);
216+
return;
217+
#endif
194218
}
195219
}
196220
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
@@ -623,6 +647,11 @@ static int semu_start(int argc, char **argv)
623647
emu.mtimer.mtimecmp = calloc(vm.n_hart, sizeof(uint64_t));
624648
emu.mswi.msip = calloc(vm.n_hart, sizeof(uint32_t));
625649
emu.sswi.ssip = calloc(vm.n_hart, sizeof(uint32_t));
650+
#if SEMU_HAS(VIRTIOSND)
651+
if (!virtio_snd_init(&(emu.vsnd)))
652+
fprintf(stderr, "No virtio-snd functioned\n");
653+
emu.vsnd.ram = emu.ram;
654+
#endif
626655

627656
/* Emulate */
628657
uint32_t peripheral_update_ctr = 0;
@@ -648,6 +677,11 @@ static int semu_start(int argc, char **argv)
648677
if (emu.vblk.InterruptStatus)
649678
emu_update_vblk_interrupts(&vm);
650679
#endif
680+
681+
#if SEMU_HAS(VIRTIOSND)
682+
if (emu.vsnd.InterruptStatus)
683+
emu_update_vsnd_interrupts(&vm);
684+
#endif
651685
}
652686

653687
emu_update_timer_interrupt(vm.hart[i]);

minimal.dts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,13 @@
6363
interrupts = <3>;
6464
};
6565
#endif
66+
67+
#if SEMU_FEATURE_VIRTIOSND
68+
snd0: virtio@4600000 {
69+
compatible = "virtio,mmio";
70+
reg = <0x4600000 0x200>;
71+
interrupts = <4>;
72+
};
73+
#endif
6674
};
6775
};

mk/check-libs.mk

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Create a mininal ALSA program
2+
define create-alsa-prog
3+
echo '\
4+
#include <alsa/asoundlib.h>\n\
5+
int main(){\n\
6+
snd_pcm_t *pcm;\n\
7+
snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);\n\
8+
snd_pcm_close(pcm);\n\
9+
return 0;\n\
10+
}\n'
11+
endef
12+
13+
# Create a mininal Core Audio program
14+
define create-coreaudio-prog
15+
echo '\
16+
#include <CoreAudio/CoreAudio.h>\n\
17+
int main(){\n\
18+
AudioComponent comp;\n\
19+
comp = AudioComponentFindNext(NULL, &desc);\n\
20+
if (comp == NULL) exit (-1);\n\
21+
return 0;\n\
22+
}\n'
23+
endef
24+
25+
# Check ALSA installation
26+
define check-alsa
27+
$(shell $(call create-alsa-prog) | $(CC) -x c -lasound -o /dev/null > /dev/null 2> /dev/null -
28+
&& echo $$?)
29+
endef
30+
31+
# Check Core Audio installation
32+
define check-coreaudio
33+
$(shell $(call create-coreaudio-prog) | $(CC) -x c -framework CoreAudio -o /dev/null > /dev/null 2> /dev/null -
34+
&& echo $$?)
35+
endef

0 commit comments

Comments
 (0)