HD Audio Emulation For Bhyve
Student: Alex Teaca (<iateaca AT SPAMFREE freebsd DOT org>)
Mentor: Alexander Motin (<mav AT SPAMFREE freebsd DOT org>), Peter Grehan (<grehan AT SPAMFREE freebsd DOT org>)
The bhyve hypervisor does not have any sound card emulation at the moment. This project aims to implement the High Definition Audio Specification which is one of the best performant and supported device in the nowadays operating systems. Some of the reasons to have this device are: support for multiple input and output streams, up to 16 channels per stream, command-response codec communication, DMA channel for each stream and it is interfaced with the PCI bus. The main goal is to have a functional implementation by the end of the summer that will allow playing sounds inside a bhyve virtual machine.
Approach to solving the problem
The main hardware blocks of the HD Audio specification are: the HD Audio Controller, the HD Audio Link, the HD Audio Codecs and the Acoustic Devices.
The HD Audio Controller is attached to the system memory via PCI, it contains one or more DMA engines and it implements all the memory mapped registers that comprise the programming interface. This module represents the main work, it will emulate the registers and implement the DMA engine in order to get the streams from the guest memory.
The Audio Link connects physically the Controller with one or more Audio codecs. Although it is a very important component for the hardware specification, in our implementation is just an interface between the Controller and the codecs.
The HD Audio Codec extracts one or more audio streams from the Audio Link and converts them to an output stream through one or more convertors. A convertor converts the digital stream to an analog signal or vice versa. Of course that in our implementation we will not convert the digital data in any analog signal but we will emulate this behavior by playing the digital stream to the /dev/dsp sound backend. The actual data stream will be played by writing to /dev/dsp and the control commands will be emulated with ioctl system calls to /dev/dsp. For example, in order to set the audio playback volume the SNDCTL_DSP_SETPLAYVOL ioctl will be used. A full list of ioctls supported by FreeBSD you can find here .
High Level Design
Even though I don’t intend to present a detailed design about the implementation, some operations are clear and concise and I will present them here because it will help me to describe the Proposal Timeline. The main modules which are inspired from the hardware specification and will be implemented are: the Audio Controller, the Audio Codec and Audio Player.
Controller Initialization - the controller is configured as a PCI device, the reset protocol is implemented and the Codec is discovered to the guest audio driver.
Codec Command and Control - the controller implements the mechanisms to send and receive control information between the guest driver and codecs which are the Command Outbound Ring Buffer (CORB) and the Response Input Ring Buffer (RIRB). The CORB and RIRB are some circular buffers located in the guest system memory that are used to pass commands from software to codecs and vice versa. The controller fetches these commands using DMA. The Codec will emulate these commands (verbs) which are documented in the HDA datasheet specification .
Stream Management - A stream is a virtual connection between a system memory buffer and the codec playing that data which is driven by a single DMA channel. The samples are collected in the guest memory into buffers which describe the Cyclic Buffer. The Buffer Descriptor List for the stream describes the starting and the length of each buffer to be played. The controller implements the mechanism to receive this data from the guest system memory using DMA. The codec will play the samples inside the buffers by writing them to the /dev/dsp.
Audio Player - its role will be to provide the codec methods to play samples and change the stream parameters using the /dev/dsp sound backend. It should be a public module inside bhyve since others sound emulations could use it in future.
At the end of the summer I will deliver the Audio Controller and Audio Codec modules emulating the High Definition Audio specification and the Audio Player module.
Implement the Controller initialization, the Codec discovery, design and prepare the data structures.
Design and implement the CORB and RIRB mechanisms in the Controller in order to get the control commands from the guest audio driver.
Emulate the control commands (verbs) from the guest audio driver.
27 June - Mid Term Evaluation
Deliver the code implemented in the first 5 weeks. The emulator will initialize the controller as a PCI device and will discover the Audio Codec to the guest audio driver. The Audio codec will emulate a subset of the control commands.
Prepare and emulate the DMA engines to get the buffers from the Cyclic Buffer inside the guest system memory.
Extract the audio samples for each channel from the data stream and implement a mechanism to prepare them to be played by the Audio Player.
Implement the Audio Player that will play samples and change the stream parameters using the /dev/dsp sound backend.
Test the HD Audio module implementation, fix the possible bugs and prepare the deliverables and the final evaluation.
Manage to implement the HDA emulation being able to play and records audio streams from a bhyve virtual machine. As planned, I developed 3 modules that work together in order to emulate the HDA stack:
- pci_hda - it registers as a PCI device and emulates each IO access to the IO ports of the HDA controller. It bridges the communication between the software (the HDA driver) and the HDA codecs by implementing the CORB and RIRB mechanisms. It also handles the stream management being responsible to transfer the audio samples from the guest system memory to the HDA codec and vice versa. It provides a strict interface with the hda_codec (see struct hda_codec_class and struct hda_ops definitions in the pci_hda.h header).
- hda_codec - it handles the control verbs issued by the software (hda codec driver), it describes and implements the structure of the hda codec (number of ADCs and DACs) and hosts the threads where the transfers are running by creating a posix thread for each audio stream.
- audio - this module provides a simple interface in order to play and record audio streams (audio_playback and audio_record) used by the hda_codec.
In order to use the HDA emulation in bhyve one must add it in the configuration line:
- -s x:y,hda,rec=/dev/dsp[%d],play=/dev/dsp[%d]
where rec=/dev/dsp[%d] provides the name of the audio device responsible with the recording from the FreeBSD host, and play=/dev/dsp[%d] provides the name of the audio device responsible with the playing to the FreeBSD host.
For example, just to have a single output audio stream one can add the below line:
- -s 5:0,hda,play=/dev/dsp0
At the moment, the HDA implementation supports 16bit bit depth and sample rates in the 16.0 - 192.0kHz range.
You can get below the patch for the HDA emulation:
Apply this patch to bhyve directory as shown below:
patch -d bhyve/ < bhyve_hda.patch
I have tested the input and output streams with 3 different guest virtual machines:
- FreeBSD 10
- Ubuntu 14.04 server
- Windows 2k8 server
In Linux I have used the aplay and arecord tools from the ALSA package in order to play and record the audio streams and mpg123 in order to play some mp3 files. In Windows I have used the Windows Media Player and Sound Recorder in order to play and record the audio streams.
Note: When play with Windows, if the sounds clarity is not good enough (there is some noise) you might need to use 'sysctl -w hw.snd.latency=0' on the FreeBSD host. The reasoning is Windows uses smaller fragments in the Cyclic Buffer in order to achieve smaller latency. To cope with it we use hw.snd.latency=0.
I have tested all supported sample rates. In Windows go to: Manage audio devices -> Playback -> Right click on the High Definition Audio Device -> Properties -> Advanced -> Choose any sample rate -> Test.
Other testing scenarios:
- start, pause, resume audio streams
- start, stop in loop
- play and record in the same time
One feature is not implemented yet is the Volumes Control. So you won't be able to control the volumes from the guest virtual machine. Anyway with the Linux and Windows guests you can still change the volumes because they can adjust the level of the volumes in software. For example in Linux, by changing the level of the Mixer PCM channel it will affect the volume of your actual stream. Similar in windows, you can change the volume per application.