diff --git a/repos/ports/lib/mk/virtualbox-devices.mk b/repos/ports/lib/mk/virtualbox-devices.mk
index 97e87dd718..03126fb711 100644
--- a/repos/ports/lib/mk/virtualbox-devices.mk
+++ b/repos/ports/lib/mk/virtualbox-devices.mk
@@ -34,6 +34,14 @@ SRC_CC += GuestHost/HGSMI/HGSMICommon.cpp
SRC_CC += Devices/Serial/DevSerial.cpp
SRC_CC += Devices/PC/DevIoApic.cpp
+SRC_C += Devices/Audio/audio.c
+SRC_C += Devices/Audio/audiosniffer.c
+SRC_C += Devices/Audio/filteraudio.c
+SRC_C += Devices/Audio/mixeng.c
+SRC_C += Devices/Audio/noaudio.c
+SRC_CC += Devices/Audio/DevIchAc97.cpp
+SRC_CC += Devices/Audio/DevIchHda.cpp
+SRC_CC += Devices/Audio/DevIchHdaCodec.cpp
SRC_CC += Devices/USB/DevOHCI.cpp
SRC_CC += Devices/USB/USBProxyDevice.cpp
SRC_CC += Devices/USB/VUSBDevice.cpp
diff --git a/repos/ports/lib/mk/virtualbox-drivers.mk b/repos/ports/lib/mk/virtualbox-drivers.mk
index 8a81deb6c9..7397647280 100644
--- a/repos/ports/lib/mk/virtualbox-drivers.mk
+++ b/repos/ports/lib/mk/virtualbox-drivers.mk
@@ -12,6 +12,10 @@ SRC_CC += Devices/Serial/DrvChar.cpp
SRC_CC += Devices/Serial/DrvRawFile.cpp
SRC_CC += Devices/Serial/DrvHostSerial.cpp
+SRC_CC += audiodrv.cpp
SRC_CC += network.cpp
-vpath network.cpp $(REP_DIR)/src/virtualbox
+INC_DIR += $(VBOX_DIR)/Devices/Audio
+
+vpath audiodrv.cpp $(REP_DIR)/src/virtualbox
+vpath network.cpp $(REP_DIR)/src/virtualbox
diff --git a/repos/ports/ports/virtualbox.hash b/repos/ports/ports/virtualbox.hash
index 12a633dffe..96cca8a61b 100644
--- a/repos/ports/ports/virtualbox.hash
+++ b/repos/ports/ports/virtualbox.hash
@@ -1 +1 @@
-59e99301300f881be06c12cd50fbe8b1164e1845
+d5121c2c57249fd1a5438b583db976d0db85c547
diff --git a/repos/ports/run/vbox_win.inc b/repos/ports/run/vbox_win.inc
index a70cc2a51a..479ffec60c 100644
--- a/repos/ports/run/vbox_win.inc
+++ b/repos/ports/run/vbox_win.inc
@@ -13,6 +13,7 @@ set overlay_image "overlay_${flavor}.vdi"
set build_components {
server/input_merger
drivers/nic
+ drivers/audio
server/nitpicker
app/vbox_pointer
server/nit_fb
@@ -22,6 +23,7 @@ set build_components {
set boot_modules {
input_merger
nic_drv
+ audio_drv
nitpicker
vbox_pointer
nit_fb
@@ -67,6 +69,24 @@ append config_of_app {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/ports/src/virtualbox/audiodrv.cpp b/repos/ports/src/virtualbox/audiodrv.cpp
new file mode 100644
index 0000000000..38025863d6
--- /dev/null
+++ b/repos/ports/src/virtualbox/audiodrv.cpp
@@ -0,0 +1,427 @@
+/*
+ * \brief Genode audio driver backend
+ * \author Josef Soentgen
+ * \date 2015-05-17
+ *
+ * TODO:
+ * - avoid excessive copying by mixing hw.mix_buf directly to
+ * Audio_out packet
+ */
+
+/*
+ * Copyright (C) 2015 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+/* VirtualBox includes */
+#include "VBoxDD.h"
+#include "vl_vbox.h"
+extern "C" {
+#include "audio.h"
+}
+#include
+
+#define AUDIO_CAP "genode"
+extern "C" {
+#include "audio_int.h"
+}
+
+
+static bool const verbose = false;
+#define PLOGV(...) if (verbose) PLOG(__VA_ARGS__);
+
+
+enum {
+ AUDIO_CHANNELS = 2,
+ AUDIO_SAMPLE_SIZE = sizeof(short),
+ AUDIO_FREQ = 44100,
+ SIZE_PER_FRAME = AUDIO_CHANNELS * AUDIO_SAMPLE_SIZE,
+};
+static const char *channel_names[] = { "front left", "front right" };
+
+
+struct GenodeVoiceOut
+{
+ HWVoiceOut hw;
+ Audio_out::Connection *audio[AUDIO_CHANNELS];
+ Audio_out::Packet *packet[AUDIO_CHANNELS];
+ uint8_t *packet_buf;
+ uint8_t *pcm_buf;
+ int wpos;
+ unsigned packets;
+ int last_submitted;
+};
+
+
+struct GenodeVoiceIn {
+ HWVoiceIn hw;
+ Audio_in::Connection *audio;
+ void *pcm_buf;
+ uint8_t *packet_buf;
+ int wpos;
+ int rpos;
+ unsigned packets;
+};
+
+
+static inline void copy_samples(void *dst, void const *src, int samples) {
+ Genode::memcpy(dst, src, samples * SIZE_PER_FRAME); }
+
+
+static int write_samples(GenodeVoiceOut *out, int16_t *src, int samples)
+{
+ int16_t *buf = (int16_t*)out->packet_buf;
+
+ /* fill and submit packet */
+ if (out->wpos >= Audio_out::PERIOD) {
+ /* alloc new packet */
+ while (true) {
+ Audio_out::Connection *c = out->audio[0];
+ try {
+ out->packet[0] = c->stream()->alloc();
+ break;
+ } catch (Audio_out::Stream::Alloc_failed) {
+ c->wait_for_alloc();
+ }
+ }
+
+ unsigned const pos = out->audio[0]->stream()->packet_position(out->packet[0]);
+ out->packet[1] = out->audio[1]->stream()->get(pos);
+
+ /* copy */
+ float *left_content = out->packet[0]->content();
+ float *right_content = out->packet[1]->content();
+
+ for (int i = 0; i < Audio_out::PERIOD; i++) {
+ left_content[i] = (float)(buf[i * AUDIO_CHANNELS + 0]) / 32768.0f;
+ right_content[i] = (float)(buf[i * AUDIO_CHANNELS + 1]) / 32768.0f;
+ }
+
+ /* submit */
+ for (int i = 0; i < AUDIO_CHANNELS; i++) {
+ out->audio[i]->submit(out->packet[i]);
+ out->packet[i] = nullptr;
+ }
+ out->last_submitted = pos;
+
+ /* move remaining samples if any to front of buffer */
+ int const remaining_samples = out->wpos - Audio_out::PERIOD;
+ if (remaining_samples) {
+ copy_samples(buf, buf + (2*Audio_out::PERIOD), remaining_samples);
+ }
+ out->wpos = remaining_samples;
+ out->packets++;
+ }
+
+ /* copy new samples */
+ copy_samples(buf + (2*out->wpos), src, samples);
+ out->wpos += samples;
+
+ return samples;
+}
+
+
+static int genode_run_out(HWVoiceOut *hw)
+{
+ GenodeVoiceOut *out = (GenodeVoiceOut *)hw;
+
+ int const live = audio_pcm_hw_get_live_out(&out->hw);
+ if (!live)
+ return 0;
+
+ int const samples = audio_MIN(live, out->hw.samples);
+ int const rpos = out->hw.rpos;
+
+ out->hw.clip(out->pcm_buf, (st_sample_t*)(out->hw.mix_buf + rpos), samples);
+ write_samples(out, (int16_t*)out->pcm_buf, samples);
+ out->hw.rpos = (rpos + samples) % out->hw.samples;
+
+ return samples;
+}
+
+
+static int genode_write(SWVoiceOut *sw, void *buf, int len) {
+ return audio_pcm_sw_write(sw, buf, len); }
+
+
+static int genode_init_out(HWVoiceOut *hw, audsettings_t *as)
+{
+ GenodeVoiceOut *out = (GenodeVoiceOut *)hw;
+
+ PLOG("--- using Audio_out session ---");
+ PLOGV("freq: %d", as->freq);
+ PLOGV("channels: %d", as->nchannels);
+ PLOGV("format: %d", as->fmt);
+ PLOGV("endianness: %d", as->endianness);
+
+ if (as->nchannels != AUDIO_CHANNELS) {
+ PERR("only %d channels supported (%d were requested)",
+ AUDIO_CHANNELS, as->nchannels);
+ return -1;
+ }
+
+ if (as->freq != AUDIO_FREQ) {
+ PERR("only %d frequency supported (%d were requested)",
+ AUDIO_FREQ, as->freq);
+ return -1;
+ }
+
+ for (int i = 0; i < AUDIO_CHANNELS; i++) {
+ try {
+ out->audio[i] = new (Genode::env()->heap())
+ Audio_out::Connection(channel_names[i]);
+ } catch (...) {
+ PERR("could not establish Audio_out connection");
+ while (--i > 0)
+ Genode::destroy(Genode::env()->heap(), out->audio[i]);
+ return -1;
+ }
+
+ out->packet[i] = nullptr;
+ }
+
+ out->wpos = 0;
+ out->last_submitted = 0;
+ out->packets = 0;
+
+ audio_pcm_init_info(&out->hw.info, as);
+ out->hw.samples = Audio_out::PERIOD;
+
+ size_t const bytes = (out->hw.samples << out->hw.info.shift);
+ out->pcm_buf = (uint8_t*)RTMemAllocZ(bytes);
+ out->packet_buf = (uint8_t*)RTMemAllocZ(2 * bytes);
+
+ return 0;
+}
+
+
+static void genode_fini_out(HWVoiceOut *hw)
+{
+ GenodeVoiceOut *out = (GenodeVoiceOut *)hw;
+
+ for (int i = 0; i < AUDIO_CHANNELS; i++)
+ Genode::destroy(Genode::env()->heap(), out->audio[i]);
+
+ RTMemFree((void*)out->pcm_buf);
+ RTMemFree((void*)out->packet_buf);
+}
+
+
+static int genode_ctl_out(HWVoiceOut *hw, int cmd, ...)
+{
+ GenodeVoiceOut *out = (GenodeVoiceOut*)hw;
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ PLOGV("enable playback");
+ out->packets = 0;
+
+ for (int i = 0; i < AUDIO_CHANNELS; i++) {
+ out->audio[i]->stream()->reset();
+ out->audio[i]->start();
+ }
+
+ break;
+ }
+ case VOICE_DISABLE:
+ {
+ PLOGV("disable playback (packets played: %u)", out->packets);
+
+ for (int i = 0; i < AUDIO_CHANNELS; i++) {
+ out->audio[i]->stop();
+ out->audio[i]->stream()->invalidate_all();
+ out->packet[i] = nullptr;
+ }
+ out->wpos = 0;
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int genode_init_in(HWVoiceIn *hw, audsettings_t *as)
+{
+ GenodeVoiceIn *in = (GenodeVoiceIn*)hw;
+
+ PLOG("--- using Audio_in session ---");
+ PLOGV("freq: %d", as->freq);
+ PLOGV("channels: %d", as->nchannels);
+ PLOGV("format: %d", as->fmt);
+ PLOGV("endianness: %d", as->endianness);
+
+ try {
+ in->audio = new (Genode::env()->heap()) Audio_in::Connection("left");
+ } catch (...) {
+ PERR("could not establish Audio_in connection");
+ return -1;
+ }
+
+ audio_pcm_init_info(&in->hw.info, as);
+ in->hw.samples = Audio_in::PERIOD;
+ in->pcm_buf = RTMemAllocZ(in->hw.samples << in->hw.info.shift);
+ in->packet_buf = (uint8_t*)RTMemAllocZ(in->hw.samples << in->hw.info.shift);
+
+ in->rpos = 0;
+ in->wpos = 0;
+ in->packets = 0;
+
+ return 0;
+}
+
+
+static void genode_fini_in(HWVoiceIn *hw)
+{
+ GenodeVoiceIn *in = (GenodeVoiceIn*)hw;
+
+ Genode::destroy(Genode::env()->heap(), in->audio);
+ RTMemFree(in->pcm_buf);
+ RTMemFree(in->packet_buf);
+}
+
+
+static int read_samples(GenodeVoiceIn *in, int16_t *dst, int samples)
+{
+ enum { PACKET_BUF_LEN = 2 * Audio_in::PERIOD };
+
+ /*
+ * Acquire next Audio_in packet first and copy the content into the
+ * packet buffer.
+ */
+ Audio_in::Stream &stream = *in->audio->stream();
+ Audio_in::Packet *p = stream.get(stream.pos());
+
+ if (!p->valid())
+ return 0;
+
+ if ((in->wpos + (Audio_in::PERIOD)) > PACKET_BUF_LEN)
+ in->wpos = 0;
+
+ int16_t *buf = (int16_t*)in->packet_buf + (in->wpos * sizeof(int16_t));
+ float *content = p->content();
+ for (int i = 0; i < PACKET_BUF_LEN; i++) {
+ int16_t const v = content[i/2] * 32767;
+ buf[i] = v;
+ buf[i+1] = v;
+ }
+
+ p->invalidate();
+ p->mark_as_recorded();
+ stream.increment_position();
+
+ in->packets++;
+ in->wpos += Audio_in::PERIOD;
+
+ /*
+ * Copy number of given samples from packet buffer to dst buffer.
+ */
+ int remaining_samples = samples;
+ if ((in->rpos + remaining_samples) > PACKET_BUF_LEN) {
+ int n = PACKET_BUF_LEN - in->rpos;
+ copy_samples(dst, buf + in->rpos, n);
+ remaining_samples -= n;
+ dst += (sizeof(int16_t) * n); /* advance in bytes */
+ in->rpos = 0;
+ }
+
+ copy_samples(dst, buf + in->rpos, remaining_samples);
+ in->rpos = (in->rpos + remaining_samples) % PACKET_BUF_LEN;
+
+ return samples;
+}
+
+
+static int genode_run_in(HWVoiceIn *hw)
+{
+ GenodeVoiceIn *in = (GenodeVoiceIn*)hw;
+
+ int const live = audio_pcm_hw_get_live_in(&in->hw);
+ if (!(in->hw.samples - live))
+ return 0;
+
+ int const dead = in->hw.samples - live;
+ int const samples = read_samples(in, (int16_t*)in->pcm_buf, dead);
+
+ in->hw.conv(in->hw.conv_buf, in->pcm_buf, samples, &pcm_in_volume);
+ in->hw.wpos = (in->hw.wpos + samples) % in->hw.samples;
+ return samples;
+}
+
+
+static int genode_read(SWVoiceIn *sw, void *buf, int size) {
+ return audio_pcm_sw_read(sw, buf, size); }
+
+
+static int genode_ctl_in(HWVoiceIn *hw, int cmd, ...)
+{
+ GenodeVoiceIn *in = (GenodeVoiceIn*)hw;
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ PLOGV("enable recording");
+ in->audio->start();
+ break;
+ }
+ case VOICE_DISABLE:
+ {
+ PLOGV("disable recording");
+ in->audio->stop();
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static void *genode_audio_init(void) {
+ return &oss_audio_driver; }
+
+
+static void genode_audio_fini(void *) { }
+
+
+static struct audio_pcm_ops genode_pcm_ops = {
+ genode_init_out,
+ genode_fini_out,
+ genode_run_out,
+ genode_write,
+ genode_ctl_out,
+
+ genode_init_in,
+ genode_fini_in,
+ genode_run_in,
+ genode_read,
+ genode_ctl_in
+};
+
+
+/*
+ * We claim to be the OSS driver so that we do not have to
+ * patch the VirtualBox source because we already claim to
+ * be FreeBSD.
+ */
+struct audio_driver oss_audio_driver = {
+ INIT_FIELD (name = ) "oss",
+ INIT_FIELD (descr = ) "Genode Audio_out/Audio_in",
+ INIT_FIELD (options = ) NULL,
+ INIT_FIELD (init = ) genode_audio_init,
+ INIT_FIELD (fini = ) genode_audio_fini,
+ INIT_FIELD (pcm_ops = ) &genode_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof(GenodeVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof(GenodeVoiceIn)
+};
diff --git a/repos/ports/src/virtualbox/devices.cc b/repos/ports/src/virtualbox/devices.cc
index de6dba8b5b..9a90838253 100644
--- a/repos/ports/src/virtualbox/devices.cc
+++ b/repos/ports/src/virtualbox/devices.cc
@@ -54,6 +54,8 @@ extern "C" int VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
REGISTER(DeviceE1000);
REGISTER(DeviceVMMDev);
REGISTER(DeviceOHCI);
+ REGISTER(DeviceICHAC97);
+ REGISTER(DeviceICH6_HDA);
return VINF_SUCCESS;
}
diff --git a/repos/ports/src/virtualbox/drivers.cc b/repos/ports/src/virtualbox/drivers.cc
index eb65a32795..07acef184d 100644
--- a/repos/ports/src/virtualbox/drivers.cc
+++ b/repos/ports/src/virtualbox/drivers.cc
@@ -33,6 +33,7 @@ extern "C" int VBoxDriversRegister(PCPDMDRVREGCB pCallbacks, uint32_t u32Version
&g_DrvVD,
&g_DrvHostInterface,
&g_DrvVUSBRootHub,
+ &g_DrvAUDIO,
0
};
diff --git a/repos/ports/src/virtualbox/libc.cc b/repos/ports/src/virtualbox/libc.cc
index bfb337e891..f44a388825 100644
--- a/repos/ports/src/virtualbox/libc.cc
+++ b/repos/ports/src/virtualbox/libc.cc
@@ -121,6 +121,7 @@ extern "C" char *getenv(const char *name)
// "+hgcm.e.l.f"
// "+shared_folders.e.l.f"
// "+drv_host_serial.e.l.f"
+// "+dev_audio.e.l.f"
;
if (Genode::strcmp(name, "VBOX_LOG_FLAGS") == 0 ||
diff --git a/repos/ports/src/virtualbox/patches/vbox_main.patch b/repos/ports/src/virtualbox/patches/vbox_main.patch
index a7bcc394cf..6df4399777 100644
--- a/repos/ports/src/virtualbox/patches/vbox_main.patch
+++ b/repos/ports/src/virtualbox/patches/vbox_main.patch
@@ -1044,16 +1044,14 @@ index caed4be..6d02496 100644
InsertConfigNode(pDevices, "AudioSniffer", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigNode(pInst, "Config", &pCfg);
-@@ -2300,9 +2311,12 @@ int Console::configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock)
+@@ -2300,9 +2311,10 @@ int Console::configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock)
/*
* AC'97 ICH / SoundBlaster16 audio / Intel HD Audio
*/
+#endif
BOOL fAudioEnabled = FALSE;
ComPtr audioAdapter;
-+#if 0
hrc = pMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); H();
-+#endif
if (audioAdapter)
hrc = audioAdapter->COMGETTER(Enabled)(&fAudioEnabled); H();