Skip to content

Commit

Permalink
Merge pull request #53 from Cuda-Chen/add-virtio-snd
Browse files Browse the repository at this point in the history
Implement VirtIO sound device playback
  • Loading branch information
jserv authored Feb 2, 2025
2 parents d61c9f6 + f473e91 commit 940a749
Show file tree
Hide file tree
Showing 14 changed files with 1,467 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ jobs:
- name: install-dependencies
run: |
sudo apt-get install build-essential device-tree-compiler expect
sudo apt-get install libasound2-dev libudev-dev
- name: default build
run: make
shell: bash
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "cnfa"]
path = cnfa
url = https://github.com/cntools/cnfa
shallow = true
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include mk/common.mk
include mk/check-libs.mk

CC ?= gcc
CFLAGS := -O2 -g -Wall -Wextra
Expand All @@ -13,6 +14,8 @@ OBJS_EXTRA :=
# command line option
OPTS :=

LDFLAGS := -lm

# virtio-blk
ENABLE_VIRTIOBLK ?= 1
$(call set-feature, VIRTIOBLK)
Expand Down Expand Up @@ -50,6 +53,43 @@ ifeq ($(call has, VIRTIONET), 1)
OBJS_EXTRA += netdev.o
endif

# virtio-snd
ENABLE_VIRTIOSND ?= 1
ifneq ($(UNAME_S),$(filter $(UNAME_S),Linux))
ENABLE_VIRTIOSND := 0
endif

# Check ALSA installation
ifeq ($(UNAME_S),Linux)
ifeq (0, $(call check-alsa))
$(warning No libasound installed. Check libasound in advance.)
ENABLE_VIRTIOSND := 0
endif
endif
$(call set-feature, VIRTIOSND)
ifeq ($(call has, VIRTIOSND), 1)
OBJS_EXTRA += virtio-snd.o

LDFLAGS += -lasound -lpthread
CFLAGS += -Icnfa

cnfa/Makefile:
git submodule update --init cnfa
cnfa/os_generic: cnfa/Makefile
$(MAKE) -C $(dir $<) os_generic.h
CNFA_LIB := cnfa/CNFA_sf.h
$(CNFA_LIB): cnfa/Makefile cnfa/os_generic
$(MAKE) -C $(dir $<) CNFA_sf.h
main.o: $(CNFA_LIB)

# suppress warning when compiling CNFA
virtio-snd.o: CFLAGS += -Wno-unused-parameter -Wno-sign-compare
endif

# .DEFAULT_GOAL should be set to all since the very first target is not all
# after git submodule.
.DEFAULT_GOAL := all

BIN = semu
all: $(BIN) minimal.dtb

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

## Prerequisites

Expand Down
1 change: 1 addition & 0 deletions cnfa
Submodule cnfa added at 60bcdd
3 changes: 3 additions & 0 deletions configs/buildroot.config
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ BR2_RELRO_NONE=y
# BR2_RELRO_PARTIAL is not set
# BR2_RELRO_FULL is not set
BR2_FORTIFY_SOURCE_1=y
BR2_PACKAGE_ALSA_UTILS=y
BR2_PACKAGE_ALSA_UTILS_APLAY=y
BR2_PACKAGE_ALSA_UTILS_SPEAKER_TEST=y
# BR2_PACKAGE_URANDOM_SCRIPTS is not set
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_FULL=y
Expand Down
7 changes: 5 additions & 2 deletions configs/linux.config
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ CONFIG_LOCALVERSION_AUTO=y
CONFIG_BUILD_SALT=""
CONFIG_DEFAULT_INIT=""
CONFIG_DEFAULT_HOSTNAME="(none)"
# CONFIG_SYSVIPC is not set
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_WATCH_QUEUE is not set
# CONFIG_CROSS_MEMORY_ATTACH is not set
Expand Down Expand Up @@ -937,7 +938,9 @@ CONFIG_DUMMY_CONSOLE_ROWS=25
# end of Console display driver support
# end of Graphics support

# CONFIG_SOUND is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_VIRTIO=y

#
# HID support
Expand Down
50 changes: 50 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,53 @@ void aclint_sswi_write(hart_t *hart,
uint8_t width,
uint32_t value);

/* VirtIO-Sound */

#if SEMU_HAS(VIRTIOSND)
#define IRQ_VSND 5
#define IRQ_VSND_BIT (1 << IRQ_VSND)

typedef struct {
uint32_t QueueNum;
uint32_t QueueDesc;
uint32_t QueueAvail;
uint32_t QueueUsed;
uint16_t last_avail;
bool ready;
} virtio_snd_queue_t;

typedef struct {
/* feature negotiation */
uint32_t DeviceFeaturesSel;
uint32_t DriverFeatures;
uint32_t DriverFeaturesSel;
/* queue config */
uint32_t QueueSel;
virtio_snd_queue_t queues[4];
/* status */
uint32_t Status;
uint32_t InterruptStatus;
/* supplied by environment */
uint32_t *ram;
/* implementation-specific */
void *priv;
} virtio_snd_state_t;

void virtio_snd_read(hart_t *core,
virtio_snd_state_t *vsnd,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_snd_write(hart_t *core,
virtio_snd_state_t *vsnd,
uint32_t addr,
uint8_t width,
uint32_t value);

bool virtio_snd_init(virtio_snd_state_t *vsnd);
#endif /* SEMU_HAS(VIRTIOSND) */

/* memory mapping */
typedef struct {
bool stopped;
Expand All @@ -326,4 +373,7 @@ typedef struct {
mtimer_state_t mtimer;
mswi_state_t mswi;
sswi_state_t sswi;
#if SEMU_HAS(VIRTIOSND)
virtio_snd_state_t vsnd;
#endif
} emu_state_t;
5 changes: 5 additions & 0 deletions feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@
#define SEMU_FEATURE_VIRTIONET 1
#endif

/* virtio-snd */
#ifndef SEMU_FEATURE_VIRTIOSND
#define SEMU_FEATURE_VIRTIOSND 1
#endif

/* Feature test macro */
#define SEMU_HAS(x) SEMU_FEATURE_##x
37 changes: 37 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ static void emu_update_swi_interrupt(hart_t *hart)
aclint_sswi_update_interrupts(hart, &data->sswi);
}

#if SEMU_HAS(VIRTIOSND)
static void emu_update_vsnd_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vsnd.InterruptStatus)
data->plic.active |= IRQ_VSND_BIT;
else
data->plic.active &= ~IRQ_VSND_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif

static void mem_load(hart_t *hart,
uint32_t addr,
uint8_t width,
Expand Down Expand Up @@ -155,6 +167,13 @@ static void mem_load(hart_t *hart,
emu_update_vrng_interrupts(hart->vm);
return;
#endif

#if SEMU_HAS(VIRTIOSND)
case 0x47: /* virtio-snd */
virtio_snd_read(hart, &data->vsnd, addr & 0xFFFFF, width, value);
emu_update_vsnd_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
Expand Down Expand Up @@ -209,12 +228,20 @@ static void mem_store(hart_t *hart,
aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value);
aclint_sswi_update_interrupts(hart, &data->sswi);
return;

#if SEMU_HAS(VIRTIORNG)
case 0x46: /* virtio-rng */
virtio_rng_write(hart, &data->vrng, addr & 0xFFFFF, width, value);
emu_update_vrng_interrupts(hart->vm);
return;
#endif

#if SEMU_HAS(VIRTIOSND)
case 0x47: /* virtio-snd */
virtio_snd_write(hart, &data->vsnd, addr & 0xFFFFF, width, value);
emu_update_vsnd_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
Expand Down Expand Up @@ -651,6 +678,11 @@ static int semu_start(int argc, char **argv)
emu.mtimer.mtimecmp = calloc(vm.n_hart, sizeof(uint64_t));
emu.mswi.msip = calloc(vm.n_hart, sizeof(uint32_t));
emu.sswi.ssip = calloc(vm.n_hart, sizeof(uint32_t));
#if SEMU_HAS(VIRTIOSND)
if (!virtio_snd_init(&(emu.vsnd)))
fprintf(stderr, "No virtio-snd functioned\n");
emu.vsnd.ram = emu.ram;
#endif

/* Emulate */
uint32_t peripheral_update_ctr = 0;
Expand All @@ -676,6 +708,11 @@ static int semu_start(int argc, char **argv)
if (emu.vblk.InterruptStatus)
emu_update_vblk_interrupts(&vm);
#endif

#if SEMU_HAS(VIRTIOSND)
if (emu.vsnd.InterruptStatus)
emu_update_vsnd_interrupts(&vm);
#endif
}

emu_update_timer_interrupt(vm.hart[i]);
Expand Down
8 changes: 8 additions & 0 deletions minimal.dts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,13 @@
interrupts = <4>;
};
#endif

#if SEMU_FEATURE_VIRTIOSND
snd0: virtio@4700000 {
compatible = "virtio,mmio";
reg = <0x4700000 0x200>;
interrupts = <5>;
};
#endif
};
};
17 changes: 17 additions & 0 deletions mk/check-libs.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Create a mininal ALSA program
define create-alsa-prog
echo '\
#include <alsa/asoundlib.h>\n\
int main(){\n\
snd_pcm_t *pcm;\n\
snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);\n\
snd_pcm_close(pcm);\n\
return 0;\n\
}\n'
endef

# Check ALSA installation
define check-alsa
$(shell $(call create-alsa-prog) | $(CC) -x c -lasound -o /dev/null > /dev/null 2> /dev/null -
&& echo $$?)
endef
Loading

0 comments on commit 940a749

Please sign in to comment.