diff --git a/repos/libports/recipes/src/vfs_oss/used_apis b/repos/libports/recipes/src/vfs_oss/used_apis
index e9aea590c1..b57036d161 100644
--- a/repos/libports/recipes/src/vfs_oss/used_apis
+++ b/repos/libports/recipes/src/vfs_oss/used_apis
@@ -1,3 +1,4 @@
+audio_in_session
audio_out_session
base
gems
diff --git a/repos/libports/run/oss.run b/repos/libports/run/oss.run
new file mode 100644
index 0000000000..e41c34ac67
--- /dev/null
+++ b/repos/libports/run/oss.run
@@ -0,0 +1,98 @@
+if {[have_board linux]} {
+ puts "Run script does not support Linux."
+ exit 0
+}
+
+if {[have_include "power_on/qemu"]} {
+ puts "Run script does not support Qemu"
+ exit 0
+}
+
+set build_components {
+ core init timer
+ drivers/audio
+ lib/vfs/oss test/oss
+}
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append_platform_drv_config
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+set boot_modules {
+ core init timer test-oss
+ ld.lib.so libc.lib.so vfs.lib.so libm.lib.so
+ posix.lib.so vfs_oss.lib.so
+}
+
+append boot_modules [audio_drv_binary]
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+run_genode_until forever
diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc
index aeb961005b..a1f4b5373f 100644
--- a/repos/libports/src/lib/libc/vfs_plugin.cc
+++ b/repos/libports/src/lib/libc/vfs_plugin.cc
@@ -7,7 +7,7 @@
*/
/*
- * Copyright (C) 2014-2017 Genode Labs GmbH
+ * Copyright (C) 2014-2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@@ -1340,17 +1340,48 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
if (!argp) return { true, EINVAL };
- /* dummy implementation */
+ monitor().monitor([&] {
+ _with_info(*fd, [&] (Xml_node info) {
+ if (info.type() != "oss") {
+ return;
+ }
- audio_buf_info &abinfo = *(audio_buf_info *)argp;
- abinfo = {
- .fragments = 4,
- .fragstotal = 256,
- .fragsize = 2048,
- .bytes = 4*2048,
- };
+ unsigned int const ifrag_size =
+ info.attribute_value("ifrag_size", 0U);
+ unsigned int const ifrag_avail =
+ info.attribute_value("ifrag_avail", 0U);
+ unsigned int const ifrag_total =
+ info.attribute_value("ifrag_total", 0U);
+ unsigned int const ifrag_bytes =
+ info.attribute_value("ifrag_bytes", 0U);
+ if (!ifrag_size || !ifrag_total) {
+ result = ENOTSUP;
+ return;
+ }
- handled = true;
+ int const fragments = (int)ifrag_avail;
+ int const fragstotal = (int)ifrag_total;
+ int const fragsize = (int)ifrag_size;
+ int const bytes = (int)ifrag_bytes;
+ if (fragments < 0 || fragstotal < 0 ||
+ fragsize < 0 || bytes < 0) {
+ result = EINVAL;
+ return;
+ }
+
+ struct audio_buf_info *buf_info =
+ (struct audio_buf_info *)argp;
+
+ buf_info->fragments = fragments;
+ buf_info->fragstotal = fragstotal;
+ buf_info->fragsize = fragsize;
+ buf_info->bytes = bytes;
+
+ handled = true;
+ });
+
+ return Fn::COMPLETE;
+ });
} else if (request == SNDCTL_DSP_GETOPTR) {
@@ -1383,6 +1414,8 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
info.attribute_value("ofrag_avail", 0U);
unsigned int const ofrag_total =
info.attribute_value("ofrag_total", 0U);
+ unsigned int const ofrag_bytes =
+ info.attribute_value("ofrag_bytes", 0U);
if (!ofrag_size || !ofrag_total) {
result = ENOTSUP;
return;
@@ -1391,7 +1424,9 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
int const fragments = (int)ofrag_avail;
int const fragstotal = (int)ofrag_total;
int const fragsize = (int)ofrag_size;
- if (fragments < 0 || fragstotal < 0 || fragsize < 0) {
+ int const bytes = (int)ofrag_bytes;
+ if (fragments < 0 || fragstotal < 0 ||
+ fragsize < 0 || bytes < 0) {
result = EINVAL;
return;
}
@@ -1402,7 +1437,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
buf_info->fragments = fragments;
buf_info->fragstotal = fragstotal;
buf_info->fragsize = fragsize;
- buf_info->bytes = fragments * fragsize;
+ buf_info->bytes = bytes;
handled = true;
});
@@ -1444,10 +1479,37 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
handled = true;
- } else if (request == SNDCTL_DSP_RESET) {
+ } else if (request == SNDCTL_DSP_HALT) {
+
+ if (((fd->flags & O_ACCMODE) == O_RDONLY) ||
+ ((fd->flags & O_ACCMODE) == O_RDWR)) {
+
+ char const halt_input_string[] = "1";
+
+ Absolute_path halt_input_path = ioctl_dir(*fd);
+ halt_input_path.append_element("halt_input");
+ File_descriptor *halt_input_fd = open(halt_input_path.base(), O_WRONLY);
+ if (!halt_input_fd)
+ return { true, ENOTSUP };
+ write(halt_input_fd, halt_input_string, sizeof(halt_input_string));
+ close(halt_input_fd);
+ }
+
+ if (((fd->flags & O_ACCMODE) == O_WRONLY) ||
+ ((fd->flags & O_ACCMODE) == O_RDWR)) {
+
+ char const halt_output_string[] = "1";
+
+ Absolute_path halt_output_path = ioctl_dir(*fd);
+ halt_output_path.append_element("halt_output");
+ File_descriptor *halt_output_fd = open(halt_output_path.base(), O_WRONLY);
+ if (!halt_output_fd)
+ return { true, ENOTSUP };
+ write(halt_output_fd, halt_output_string, sizeof(halt_output_string));
+ close(halt_output_fd);
+ }
handled = true;
- warning("SNDCTL_DSP_RESET handled=", handled, " result=", result);
} else if (request == SNDCTL_DSP_SAMPLESIZE) {
@@ -1487,54 +1549,111 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
int max_fragments = *frag >> 16;
int size_selector = *frag & ((1<<16) - 1);
- char ofrag_total_string[16];
- char ofrag_size_string[16];
+ if (((fd->flags & O_ACCMODE) == O_RDONLY) ||
+ ((fd->flags & O_ACCMODE) == O_RDWR)) {
- ::snprintf(ofrag_total_string, sizeof(ofrag_total_string),
- "%u", max_fragments);
+ char ifrag_total_string[16];
+ char ifrag_size_string[16];
- ::snprintf(ofrag_size_string, sizeof(ofrag_size_string),
- "%u", 1 << size_selector);
+ ::snprintf(ifrag_total_string, sizeof(ifrag_total_string),
+ "%u", max_fragments);
- Absolute_path ofrag_total_path = ioctl_dir(*fd);
- ofrag_total_path.append_element("ofrag_total");
- File_descriptor *ofrag_total_fd = open(ofrag_total_path.base(), O_RDWR);
- if (!ofrag_total_fd)
- return { true, ENOTSUP };
- write(ofrag_total_fd, ofrag_total_string, sizeof(ofrag_total_string));
- close(ofrag_total_fd);
+ ::snprintf(ifrag_size_string, sizeof(ifrag_size_string),
+ "%u", 1 << size_selector);
- Absolute_path ofrag_size_path = ioctl_dir(*fd);
- ofrag_size_path.append_element("ofrag_size");
- File_descriptor *ofrag_size_fd = open(ofrag_size_path.base(), O_RDWR);
- if (!ofrag_size_fd)
- return { true, ENOTSUP };
- write(ofrag_size_fd, ofrag_size_string, sizeof(ofrag_size_string));
- close(ofrag_size_fd);
+ Absolute_path ifrag_total_path = ioctl_dir(*fd);
+ ifrag_total_path.append_element("ifrag_total");
+ File_descriptor *ifrag_total_fd = open(ifrag_total_path.base(), O_RDWR);
+ if (!ifrag_total_fd)
+ return { true, ENOTSUP };
+ write(ifrag_total_fd, ifrag_total_string, sizeof(ifrag_total_string));
+ close(ifrag_total_fd);
- monitor().monitor([&] {
+ Absolute_path ifrag_size_path = ioctl_dir(*fd);
+ ifrag_size_path.append_element("ifrag_size");
+ File_descriptor *ifrag_size_fd = open(ifrag_size_path.base(), O_RDWR);
+ if (!ifrag_size_fd)
+ return { true, ENOTSUP };
+ write(ifrag_size_fd, ifrag_size_string, sizeof(ifrag_size_string));
+ close(ifrag_size_fd);
- _with_info(*fd, [&] (Xml_node info) {
- if (info.type() != "oss") {
- return;
- }
+ monitor().monitor([&] {
- unsigned int const ofrag_size =
- info.attribute_value("ofrag_size", 0U);
- unsigned int const ofrag_size_log2 =
- ofrag_size ? Genode::log2(ofrag_size) : 0;
+ _with_info(*fd, [&] (Xml_node info) {
+ if (info.type() != "oss") {
+ return;
+ }
- unsigned int const ofrag_total =
- info.attribute_value("ofrag_total", 0U);
+ unsigned int const ifrag_size =
+ info.attribute_value("ifrag_size", 0U);
+ unsigned int const ifrag_size_log2 =
+ ifrag_size ? Genode::log2(ifrag_size) : 0;
- if (!ofrag_total || !ofrag_size_log2) {
- result = ENOTSUP;
- return;
- }
+ unsigned int const ifrag_total =
+ info.attribute_value("ifrag_total", 0U);
+
+ if (!ifrag_total || !ifrag_size_log2) {
+ result = ENOTSUP;
+ return;
+ }
+ });
+
+ return Fn::COMPLETE;
});
+ }
- return Fn::COMPLETE;
- });
+ if (((fd->flags & O_ACCMODE) == O_WRONLY) ||
+ ((fd->flags & O_ACCMODE) == O_RDWR)) {
+
+ char ofrag_total_string[16];
+ char ofrag_size_string[16];
+
+ ::snprintf(ofrag_total_string, sizeof(ofrag_total_string),
+ "%u", max_fragments);
+
+ ::snprintf(ofrag_size_string, sizeof(ofrag_size_string),
+ "%u", 1 << size_selector);
+
+ Absolute_path ofrag_total_path = ioctl_dir(*fd);
+ ofrag_total_path.append_element("ofrag_total");
+ File_descriptor *ofrag_total_fd = open(ofrag_total_path.base(), O_RDWR);
+ if (!ofrag_total_fd)
+ return { true, ENOTSUP };
+ write(ofrag_total_fd, ofrag_total_string, sizeof(ofrag_total_string));
+ close(ofrag_total_fd);
+
+ Absolute_path ofrag_size_path = ioctl_dir(*fd);
+ ofrag_size_path.append_element("ofrag_size");
+ File_descriptor *ofrag_size_fd = open(ofrag_size_path.base(), O_RDWR);
+ if (!ofrag_size_fd)
+ return { true, ENOTSUP };
+ write(ofrag_size_fd, ofrag_size_string, sizeof(ofrag_size_string));
+ close(ofrag_size_fd);
+
+ monitor().monitor([&] {
+
+ _with_info(*fd, [&] (Xml_node info) {
+ if (info.type() != "oss") {
+ return;
+ }
+
+ unsigned int const ofrag_size =
+ info.attribute_value("ofrag_size", 0U);
+ unsigned int const ofrag_size_log2 =
+ ofrag_size ? Genode::log2(ofrag_size) : 0;
+
+ unsigned int const ofrag_total =
+ info.attribute_value("ofrag_total", 0U);
+
+ if (!ofrag_total || !ofrag_size_log2) {
+ result = ENOTSUP;
+ return;
+ }
+ });
+
+ return Fn::COMPLETE;
+ });
+ }
handled = true;
@@ -1641,8 +1760,6 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
* Either handled or a failed attempt will mark the I/O control
* as handled.
*/
- if (request == SNDCTL_DSP_RESET)
- warning("SNDCTL_DSP_RESET handled=", handled, " result=", result);
return { handled || result != 0, result };
}
diff --git a/repos/libports/src/lib/vfs/oss/README b/repos/libports/src/lib/vfs/oss/README
index 2b86bb9e64..3f66208634 100644
--- a/repos/libports/src/lib/vfs/oss/README
+++ b/repos/libports/src/lib/vfs/oss/README
@@ -1,16 +1,17 @@
-The VFS OSS plugin offers access to Genode's Audio_out session by providing a
-file-system that can be mounted at arbitrary location within the VFS of a
-component. It exposes a data file that can be used as 'dsp' file, e.g., _/dev/dsp_
-as is common with OSS. The support I/O control operations or rather the
+The VFS OSS plugin offers access to Genode's Audio_out and Audio_in sessions by
+providing a file-system that can be mounted at arbitrary location within the VFS
+of a component. It exposes a data file that can be used as 'dsp' file, e.g.,
+_/dev/dsp_ as is common with OSS. The support I/O control operations or rather the
properties of the pseudo-device are provided in form of a structured 'info'
file located in the directory named after the data file, e.g., _/dev/.dsp/info_.
This file may by used to query the configured parameters and has the following
structure:
-!
+!
Each parameter can also be accessed via its own file. The following list
presents all files:
@@ -18,11 +19,25 @@ presents all files:
* :channels (ro): number of available channels. Set to 2 (stereo).
Corresponding OSS commands: 'SNDCTL_DSP_CHANNELS'
+ * :format (ro): sample format, e.g. s16le. Defaults to AFMT_S16_LE.
+ Corresponding OSS commands: 'SNDCTL_DSP_SAMPLESIZE'
+
* :sample_rate (ro): sample rate of the underlying Audio_out session.
Corresponding OSS commands: 'SNDCTL_DSP_SPEED'
- * :format (ro): sample format, e.g. s16le. Defaults to AFMT_S16_LE.
- Corresponding OSS commands: 'SNDCTL_DSP_SAMPLESIZE'
+ * :ifrag_total (rw): total number of input fragments. Set to number of packets
+ in the underlying Audio_in session's packet-stream by default.
+ Corresponding OSS commands: 'SNDCTL_DSP_SETFRAGMENT', 'SNDCTL_DSP_GETISPACE'
+
+ * :ifrag_size (rw): size of an input fragment. Set to 2048 (number of channels
+ times size of Audio_in period times size of s16le sample) by default.
+ Corresponding OSS commands: 'SNDCTL_DSP_SETFRAGMENT', 'SNDCTL_DSP_GETISPACE'
+
+ * :ifrag_avail (ro): number of available input fragments. Initially set to 0.
+ Corresponding OSS commands: 'SNDCTL_DSP_GETISPACE'
+
+ * :ifrag_bytes (ro): number of available input bytes. Initially set to 0.
+ Corresponding OSS commands: 'SNDCTL_DSP_GETISPACE'
* :ofrag_total (rw): total number of output fragments. Set to number of packets
in the underlying Audio_out session's packet-stream by default.
@@ -36,6 +51,10 @@ presents all files:
fragment count.
Corresponding OSS commands: 'SNDCTL_DSP_GETOSPACE'
+ * :ofrag_bytes (ro): number of available output bytes. Initially set to total
+ count buffer space.
+ Corresponding OSS commands: 'SNDCTL_DSP_GETOSPACE'
+
* :optr_samples (ro): total number of samples submitted to the Audio_out session
Corresponding OSS commands: 'SNDCTL_DSP_CURRENT_OPTR'
@@ -46,7 +65,13 @@ presents all files:
into this file resets the value to zero
Corresponding OSS commands: 'SNDCTL_DSP_GETERROR'
-In its current state it is merely enough to use simple applications requiring
+ * halt_input (wo): writing anything into this file halts input processing.
+ Corresponding OSS commands: SNDCTL_DSP_HALT
+
+ * halt_output (wo): writing anything into this file halts output processing.
+ Corresponding OSS commands: SNDCTL_DSP_HALT
+
+In its current state the plugin is merely enough to use simple applications requiring
nothing more than a minimal set of the OSSv4 API. It does not allow altering
of all parameters and will only work when 44100Hz/s16le is used.
diff --git a/repos/libports/src/lib/vfs/oss/vfs_oss.cc b/repos/libports/src/lib/vfs/oss/vfs_oss.cc
index bcb0aa460a..89296bc579 100644
--- a/repos/libports/src/lib/vfs/oss/vfs_oss.cc
+++ b/repos/libports/src/lib/vfs/oss/vfs_oss.cc
@@ -1,17 +1,18 @@
/*
- * \brief OSS emulation to Audio_out file system
+ * \brief OSS emulation to Audio_out and Audio_in file systems
* \author Josef Soentgen
* \date 2018-10-25
*/
/*
- * Copyright (C) 2018-2020 Genode Labs GmbH
+ * Copyright (C) 2018-2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
+#include
#include
#include
#include
@@ -25,9 +26,28 @@
/* libc includes */
#include
+static constexpr bool verbose_underrun { false };
-static constexpr size_t _stream_packet_size { Audio_out::PERIOD * Audio_out::SAMPLE_SIZE };
+static constexpr size_t _audio_in_stream_packet_size
+{ Audio_in::PERIOD * Audio_in::SAMPLE_SIZE };
+static constexpr size_t _audio_out_stream_packet_size
+{ Audio_out::PERIOD * Audio_out::SAMPLE_SIZE };
+
+/*
+ * One packet cannot be allocated because of the ring buffer
+ * implementation.
+ */
+static constexpr size_t _audio_in_stream_size { (Audio_in::QUEUE_SIZE - 1) *
+ _audio_in_stream_packet_size };
+
+/*
+ * One packet cannot be allocated because of the ring buffer
+ * implementation, another packet cannot be allocated after
+ * the stream is reset by 'Audio_out::Session_client::start()'.
+ */
+static constexpr size_t _audio_out_stream_size { (Audio_out::QUEUE_SIZE - 2) *
+ _audio_out_stream_packet_size };
namespace Vfs { struct Oss_file_system; }
@@ -53,9 +73,14 @@ struct Vfs::Oss_file_system::Audio
unsigned channels;
unsigned format;
unsigned sample_rate;
+ unsigned ifrag_total;
+ unsigned ifrag_size;
+ unsigned ifrag_avail;
+ unsigned ifrag_bytes;
unsigned ofrag_total;
unsigned ofrag_size;
unsigned ofrag_avail;
+ unsigned ofrag_bytes;
long long optr_samples;
unsigned optr_fifo_samples;
unsigned play_underruns;
@@ -63,9 +88,14 @@ struct Vfs::Oss_file_system::Audio
Readonly_value_file_system &_channels_fs;
Readonly_value_file_system &_format_fs;
Readonly_value_file_system &_sample_rate_fs;
+ Value_file_system &_ifrag_total_fs;
+ Value_file_system &_ifrag_size_fs;
+ Readonly_value_file_system &_ifrag_avail_fs;
+ Readonly_value_file_system &_ifrag_bytes_fs;
Value_file_system &_ofrag_total_fs;
Value_file_system &_ofrag_size_fs;
Readonly_value_file_system &_ofrag_avail_fs;
+ Readonly_value_file_system &_ofrag_bytes_fs;
Readonly_value_file_system &_optr_samples_fs;
Readonly_value_file_system &_optr_fifo_samples_fs;
Value_file_system &_play_underruns_fs;
@@ -73,9 +103,14 @@ struct Vfs::Oss_file_system::Audio
Info(Readonly_value_file_system &channels_fs,
Readonly_value_file_system &format_fs,
Readonly_value_file_system &sample_rate_fs,
+ Value_file_system &ifrag_total_fs,
+ Value_file_system &ifrag_size_fs,
+ Readonly_value_file_system &ifrag_avail_fs,
+ Readonly_value_file_system &ifrag_bytes_fs,
Value_file_system &ofrag_total_fs,
Value_file_system &ofrag_size_fs,
Readonly_value_file_system &ofrag_avail_fs,
+ Readonly_value_file_system &ofrag_bytes_fs,
Readonly_value_file_system &optr_samples_fs,
Readonly_value_file_system &optr_fifo_samples_fs,
Value_file_system &play_underruns_fs)
@@ -83,18 +118,28 @@ struct Vfs::Oss_file_system::Audio
channels { 0 },
format { 0 },
sample_rate { 0 },
+ ifrag_total { 0 },
+ ifrag_size { 0 },
+ ifrag_avail { 0 },
+ ifrag_bytes { 0 },
ofrag_total { 0 },
ofrag_size { 0 },
ofrag_avail { 0 },
+ ofrag_bytes { 0 },
optr_samples { 0 },
optr_fifo_samples { 0 },
play_underruns { 0 },
_channels_fs { channels_fs },
_format_fs { format_fs },
_sample_rate_fs { sample_rate_fs },
+ _ifrag_total_fs { ifrag_total_fs },
+ _ifrag_size_fs { ifrag_size_fs },
+ _ifrag_avail_fs { ifrag_avail_fs },
+ _ifrag_bytes_fs { ifrag_bytes_fs },
_ofrag_total_fs { ofrag_total_fs },
_ofrag_size_fs { ofrag_size_fs },
_ofrag_avail_fs { ofrag_avail_fs },
+ _ofrag_bytes_fs { ofrag_bytes_fs },
_optr_samples_fs { optr_samples_fs },
_optr_fifo_samples_fs { optr_fifo_samples_fs },
_play_underruns_fs { play_underruns_fs }
@@ -105,9 +150,14 @@ struct Vfs::Oss_file_system::Audio
_channels_fs .value(channels);
_format_fs .value(format);
_sample_rate_fs .value(sample_rate);
+ _ifrag_total_fs .value(ifrag_total);
+ _ifrag_size_fs .value(ifrag_size);
+ _ifrag_avail_fs .value(ifrag_avail);
+ _ifrag_bytes_fs .value(ifrag_bytes);
_ofrag_total_fs .value(ofrag_total);
_ofrag_size_fs .value(ofrag_size);
_ofrag_avail_fs .value(ofrag_avail);
+ _ofrag_bytes_fs .value(ofrag_bytes);
_optr_samples_fs .value(optr_samples);
_optr_fifo_samples_fs.value(optr_fifo_samples);
_play_underruns_fs .value(play_underruns);
@@ -115,15 +165,20 @@ struct Vfs::Oss_file_system::Audio
void print(Genode::Output &out) const
{
- char buf[256] { };
+ char buf[512] { };
Genode::Xml_generator xml(buf, sizeof(buf), "oss", [&] () {
xml.attribute("channels", channels);
xml.attribute("format", format);
xml.attribute("sample_rate", sample_rate);
+ xml.attribute("ifrag_total", ifrag_total);
+ xml.attribute("ifrag_size", ifrag_size);
+ xml.attribute("ifrag_avail", ifrag_avail);
+ xml.attribute("ifrag_bytes", ifrag_bytes);
xml.attribute("ofrag_total", ofrag_total);
xml.attribute("ofrag_size", ofrag_size);
xml.attribute("ofrag_avail", ofrag_avail);
+ xml.attribute("ofrag_bytes", ofrag_bytes);
xml.attribute("optr_samples", optr_samples);
xml.attribute("optr_fifo_samples", optr_fifo_samples);
xml.attribute("play_underruns", play_underruns);
@@ -138,21 +193,26 @@ struct Vfs::Oss_file_system::Audio
Audio(Audio const &);
Audio &operator = (Audio const &);
- bool _started { false };
+ bool _audio_out_started { false };
+ bool _audio_in_started { false };
enum { CHANNELS = 2, };
const char *_channel_names[CHANNELS] = { "front left", "front right" };
Genode::Constructible _out[CHANNELS];
+ Genode::Constructible _in { };
Info &_info;
- Readonly_value_file_system &_info_fs;
+ Readonly_value_file_system &_info_fs;
+
+ size_t _read_sample_offset { 0 };
+ size_t _write_sample_offset { 0 };
public:
Audio(Genode::Env &env,
Info &info,
- Readonly_value_file_system &info_fs)
+ Readonly_value_file_system &info_fs)
:
_info { info },
_info_fs { info_fs }
@@ -166,59 +226,98 @@ struct Vfs::Oss_file_system::Audio
}
}
+ try {
+ _in.construct(env, "left");
+ } catch (...) {
+ Genode::error("could not create Audio_in channel");
+ throw;
+ }
+
_info.channels = CHANNELS;
_info.format = (unsigned)AFMT_S16_LE;
_info.sample_rate = Audio_out::SAMPLE_RATE;
- _info.ofrag_total = Audio_out::QUEUE_SIZE - 2;
- _info.ofrag_size =
- (unsigned)Audio_out::PERIOD * (unsigned)CHANNELS
- * sizeof (int16_t);
+ _info.ifrag_size = 2048;
+ _info.ifrag_total = _audio_in_stream_size / _info.ifrag_size;
+ _info.ifrag_avail = 0;
+ _info.ifrag_bytes = 0;
+ _info.ofrag_size = 2048;
+ _info.ofrag_total = _audio_out_stream_size / _info.ofrag_size;
_info.ofrag_avail = _info.ofrag_total;
+ _info.ofrag_bytes = _info.ofrag_avail * _info.ofrag_size;
_info.update();
_info_fs.value(_info);
}
- void alloc_sigh(Genode::Signal_context_capability sigh)
- {
- _out[0]->alloc_sigh(sigh);
- }
-
- void progress_sigh(Genode::Signal_context_capability sigh)
+ void out_progress_sigh(Genode::Signal_context_capability sigh)
{
_out[0]->progress_sigh(sigh);
}
- void pause()
+ void in_progress_sigh(Genode::Signal_context_capability sigh)
{
- for (int i = 0; i < CHANNELS; i++) {
- _out[i]->stop();
- }
-
- _started = false;
+ _in->progress_sigh(sigh);
}
- unsigned queued() const
+ void in_overrun_sigh(Genode::Signal_context_capability sigh)
{
- return _out[0]->stream()->queued();
+ _in->overrun_sigh(sigh);
+ }
+
+ bool read_ready()
+ {
+ return _info.ifrag_bytes > 0;
}
void update_info_ofrag_avail_from_optr_fifo_samples()
{
- unsigned const samples_per_fragment =
- _info.ofrag_size / (CHANNELS * sizeof(int16_t));
- _info.ofrag_avail = _info.ofrag_total -
- ((_info.optr_fifo_samples / samples_per_fragment) +
- ((_info.optr_fifo_samples % samples_per_fragment) ? 1 : 0));
+ _info.ofrag_bytes = (_info.ofrag_total * _info.ofrag_size) -
+ ((_info.optr_fifo_samples + _write_sample_offset) *
+ CHANNELS * sizeof(int16_t));
+ _info.ofrag_avail = _info.ofrag_bytes / _info.ofrag_size;
+
+ _info.update();
+ _info_fs.value(_info);
+ }
+
+ void halt_input()
+ {
+ if (_audio_in_started) {
+ _in->stop();
+ _in->stream()->reset();
+ _read_sample_offset = 0;
+ _audio_in_started = false;
+ update_info_ifrag_avail();
+ }
+ }
+
+ void halt_output()
+ {
+ if (_audio_out_started) {
+ for (int i = 0; i < CHANNELS; i++)
+ _out[i]->stop();
+ _write_sample_offset = 0;
+ _audio_out_started = false;
+ _info.optr_fifo_samples = 0;
+ update_info_ofrag_avail_from_optr_fifo_samples();
+ }
}
/*
- * Handle progress signal.
+ * Handle Audio_out progress signal.
*
* Returns true if at least one stream packet is available.
*/
- bool handle_progress()
+ bool handle_out_progress()
{
- unsigned fifo_samples_new = queued() * Audio_out::PERIOD;
+ unsigned fifo_samples_new = _out[0]->stream()->queued() *
+ Audio_out::PERIOD;
+
+ if ((fifo_samples_new >= Audio_out::PERIOD) &&
+ (_write_sample_offset != 0)) {
+ /* an allocated packet is part of the queued count,
+ but might not have been submitted yet */
+ fifo_samples_new -= Audio_out::PERIOD;
+ }
if (fifo_samples_new == _info.optr_fifo_samples) {
/*
@@ -232,21 +331,142 @@ struct Vfs::Oss_file_system::Audio
* The queue count can wrap from 0 to 255 if packets are not
* submitted fast enough.
*/
-
if ((fifo_samples_new == 0) ||
(fifo_samples_new > _info.optr_fifo_samples)) {
- pause();
+
+ halt_output();
+
+ _write_sample_offset = 0;
+
if (fifo_samples_new > _info.optr_fifo_samples) {
_info.play_underruns++;
fifo_samples_new = 0;
}
+
+ if (verbose_underrun) {
+ static int play_underruns_total;
+ play_underruns_total++;
+ Genode::warning("vfs_oss: underrun (",
+ play_underruns_total, ")");
+ }
}
_info.optr_fifo_samples = fifo_samples_new;
update_info_ofrag_avail_from_optr_fifo_samples();
+
+ return true;
+ }
+
+ void update_info_ifrag_avail()
+ {
+ unsigned max_queued = (_info.ifrag_total * _info.ifrag_size) /
+ _audio_in_stream_packet_size;
+ unsigned queued = _in->stream()->queued();
+
+ if (queued > max_queued) {
+
+ /*
+ * Reset tail pointer to end of configured buffer
+ * to stay in bounds of the configuration.
+ */
+ unsigned pos = _in->stream()->pos();
+ for (unsigned int i = 0; i < max_queued; i++)
+ _in->stream()->increment_position();
+
+ _in->stream()->reset();
+ _in->stream()->pos(pos);
+ }
+
+ _info.ifrag_bytes = min((_in->stream()->queued() * _audio_in_stream_packet_size) -
+ (_read_sample_offset * Audio_in::SAMPLE_SIZE),
+ _info.ifrag_total * _info.ifrag_size);
+ _info.ifrag_avail = _info.ifrag_bytes / _info.ifrag_size;
_info.update();
_info_fs.value(_info);
+ }
+ /*
+ * Handle Audio_in progress signal.
+ *
+ * Returns true if at least one stream packet is available.
+ */
+ bool handle_in_progress()
+ {
+ if (_audio_in_started) {
+ update_info_ifrag_avail();
+ return _info.ifrag_bytes > 0;
+ }
+
+ return false;
+ }
+
+ bool read(char *buf, file_size buf_size, file_size &out_size)
+ {
+ out_size = 0;
+
+ if (!_audio_in_started) {
+ _in->start();
+ _audio_in_started = true;
+ }
+
+ if (_info.ifrag_bytes == 0) {
+ /* block */
+ return true;
+ }
+
+ buf_size = min(buf_size, _info.ifrag_bytes);
+
+ unsigned samples_to_read = buf_size / CHANNELS / sizeof(int16_t);
+
+ if (samples_to_read == 0) {
+ /* invalid argument */
+ return false;
+ }
+
+ Audio_in::Stream *stream = _in->stream();
+
+ unsigned samples_read = 0;
+
+ /* packet loop */
+
+ for (;;) {
+ unsigned stream_pos = stream->pos();
+
+ Audio_in::Packet *p = stream->get(stream_pos);
+
+ if (!p || !p->valid()) {
+ update_info_ifrag_avail();
+ return true;
+ }
+
+ /* sample loop */
+
+ for (;;) {
+
+ if (samples_read == samples_to_read) {
+ update_info_ifrag_avail();
+ return true;
+ }
+
+ for (unsigned c = 0; c < CHANNELS; c++) {
+ unsigned const buf_index = out_size / sizeof(int16_t);
+ ((int16_t*)buf)[buf_index] = p->content()[_read_sample_offset] * 32768;
+ out_size += sizeof(int16_t);
+ }
+
+ samples_read++;
+
+ _read_sample_offset++;
+
+ if (_read_sample_offset == Audio_in::PERIOD) {
+ p->invalidate();
+ p->mark_as_recorded();
+ stream->increment_position();
+ _read_sample_offset = 0;
+ break;
+ }
+ }
+ }
return true;
}
@@ -254,122 +474,106 @@ struct Vfs::Oss_file_system::Audio
{
using namespace Genode;
+ out_size = 0;
+
+ if (_info.ofrag_bytes == 0)
+ throw Vfs::File_io_service::Insufficient_buffer();
+
bool block_write = false;
- /*
- * Calculate how many strean packets would be needed to
- * write the buffer.
- */
-
- unsigned stream_packets_to_write =
- (buf_size / _stream_packet_size) +
- ((buf_size % _stream_packet_size != 0) ? 1 : 0);
-
- /*
- * Calculate how many stream packets are available
- * depending on the configured fragment count and
- * fragment size and the number of packets already
- * in use.
- */
-
- unsigned const stream_packets_total =
- (_info.ofrag_total * _info.ofrag_size) / _stream_packet_size;
-
- unsigned const stream_packets_used =
- _info.optr_fifo_samples / Audio_out::PERIOD;
-
- unsigned const stream_packets_avail =
- stream_packets_total - stream_packets_used;
-
- /*
- * If not enough stream packets are available, use the
- * available packets and report the blocking condition
- * to the caller.
- */
-
- if (stream_packets_to_write > stream_packets_avail) {
- stream_packets_to_write = stream_packets_avail;
- buf_size = stream_packets_to_write * _stream_packet_size;
+ if (buf_size > _info.ofrag_bytes) {
+ buf_size = _info.ofrag_bytes;
block_write = true;
}
- if (stream_packets_to_write == 0) {
- out_size = 0;
- throw Vfs::File_io_service::Insufficient_buffer();
+ unsigned stream_samples_to_write = buf_size / CHANNELS / sizeof(int16_t);
+
+ if (stream_samples_to_write == 0) {
+ /* invalid argument */
+ return false;
}
- if (!_started) {
- _started = true;
+ if (!_audio_out_started) {
+ _audio_out_started = true;
_out[0]->start();
_out[1]->start();
}
- for (unsigned packet_count = 0;
- packet_count < stream_packets_to_write;
- packet_count++) {
+ unsigned stream_samples_written = 0;
+
+ /* packet loop */
+
+ for (;;) {
Audio_out::Packet *lp = nullptr;
- try { lp = _out[0]->stream()->alloc(); }
- catch (...) {
- error("stream full",
- " queued: ", _out[0]->stream()->queued(),
- " pos: ", _out[0]->stream()->pos(),
- " tail: ", _out[0]->stream()->tail()
- );
- break;
- }
+ if (_write_sample_offset == 0) {
+
+ for (;;) {
+ try {
+ lp = _out[0]->stream()->alloc();
+ break;
+ }
+ catch (...) {
+ /* this can happen on underrun */
+ _out[0]->stream()->reset();
+ }
+ }
+ } else {
+ /*
+ * Look up the previously allocated packet.
+ * The tail pointer got incremented after allocation,
+ * so we need to decrement by 1.
+ */
+ unsigned const tail =
+ (_out[0]->stream()->tail() +
+ Audio_out::QUEUE_SIZE - 1) %
+ Audio_out::QUEUE_SIZE;
+ lp = _out[0]->stream()->get(tail);
+ }
unsigned const pos = _out[0]->stream()->packet_position(lp);
Audio_out::Packet *rp = _out[1]->stream()->get(pos);
float *dest[CHANNELS] = { lp->content(), rp->content() };
- for (unsigned sample_count = 0;
- sample_count < Audio_out::PERIOD;
- sample_count++) {
+ /* sample loop */
+
+ for (;;) {
for (unsigned c = 0; c < CHANNELS; c++) {
+ unsigned const buf_index = out_size / sizeof(int16_t);
+ int16_t src_sample = ((int16_t const*)buf)[buf_index];
+ dest[c][_write_sample_offset] = ((float)src_sample) / 32768.0f;
+ out_size += sizeof(int16_t);
+ }
- unsigned const buf_index =
- (packet_count * Audio_out::PERIOD * CHANNELS) +
- (sample_count * CHANNELS) + c;
+ stream_samples_written++;
- int16_t src_sample;
- if (buf_index * sizeof(uint16_t) < buf_size) {
- src_sample = ((int16_t const*)buf)[buf_index];
- } else {
- /*
- * Fill up the packet with zeroes if the buffer
- * is not aligned to minimum fragment size
- * (packet size) granularity.
- */
- src_sample = 0;
- }
+ _write_sample_offset++;
- dest[c][sample_count] = ((float)src_sample) / 32768.0f;
+ if (_write_sample_offset == Audio_out::PERIOD) {
+ _info.optr_samples += Audio_out::PERIOD;
+ _info.optr_fifo_samples += Audio_out::PERIOD;
+ _out[0]->submit(lp);
+ _out[1]->submit(rp);
+ _write_sample_offset = 0;
+ if (stream_samples_written != stream_samples_to_write)
+ break;
+ }
+
+ if (stream_samples_written == stream_samples_to_write) {
+
+ /* update info */
+ update_info_ofrag_avail_from_optr_fifo_samples();
+
+ if (block_write) { throw Vfs::File_io_service::Insufficient_buffer(); }
+
+ return true;
}
}
-
- _out[0]->submit(lp);
- _out[1]->submit(rp);
}
- out_size = Genode::min(stream_packets_to_write *
- _stream_packet_size, buf_size);
-
- /* update info */
-
- unsigned const stream_samples_written =
- stream_packets_to_write * Audio_out::PERIOD;
- _info.optr_samples += stream_samples_written;
- _info.optr_fifo_samples += stream_samples_written;
- update_info_ofrag_avail_from_optr_fifo_samples();
- _info.update();
- _info_fs.value(_info);
-
- if (block_write) { throw Vfs::File_io_service::Insufficient_buffer(); }
-
return true;
}
};
@@ -403,14 +607,24 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
Read_result read(char *buf, file_size buf_size, file_size &out_count) override
{
- /* dummy implementation with audible noise for testing */
+ if (!buf)
+ return READ_ERR_INVALID;
- for (file_size i = 0; i < buf_size; i++)
- buf[i] = i;
+ if (buf_size == 0) {
+ out_count = 0;
+ return READ_OK;
+ }
- out_count = buf_size;
+ bool success = _audio.read(buf, buf_size, out_count);
- return READ_OK;
+ if (success) {
+ if (out_count == 0) {
+ blocked = true;
+ return READ_QUEUED;
+ }
+ return READ_OK;
+ }
+ return READ_ERR_INVALID;
}
Write_result write(char const *buf, file_size buf_size,
@@ -426,7 +640,7 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
bool read_ready() override
{
- return true;
+ return _audio.read_ready();
}
};
@@ -435,17 +649,28 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
Handle_registry _handle_registry { };
- Genode::Io_signal_handler _alloc_avail_sigh {
- _ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_alloc_avail };
+ Genode::Io_signal_handler _audio_out_progress_sigh {
+ _ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_audio_out_progress };
- void _handle_alloc_avail() { }
+ Genode::Io_signal_handler _audio_in_progress_sigh {
+ _ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_audio_in_progress };
- Genode::Io_signal_handler _progress_sigh {
- _ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_progress };
-
- void _handle_progress()
+ void _handle_audio_out_progress()
{
- if (_audio.handle_progress()) {
+ if (_audio.handle_out_progress()) {
+ /* at least one stream packet is available */
+ _handle_registry.for_each([this] (Registered_handle &handle) {
+ if (handle.blocked) {
+ handle.blocked = false;
+ handle.io_progress_response();
+ }
+ });
+ }
+ }
+
+ void _handle_audio_in_progress()
+ {
+ if (_audio.handle_in_progress()) {
/* at least one stream packet is available */
_handle_registry.for_each([this] (Registered_handle &handle) {
if (handle.blocked) {
@@ -468,8 +693,8 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
_ep { ep },
_audio { audio }
{
- _audio.alloc_sigh(_alloc_avail_sigh);
- _audio.progress_sigh(_progress_sigh);
+ _audio.out_progress_sigh(_audio_out_progress_sigh);
+ _audio.in_progress_sigh(_audio_in_progress_sigh);
}
static const char *name() { return "data"; }
@@ -521,25 +746,62 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
Vfs::Env &_env;
+ /* RO/RW files */
Readonly_value_file_system _channels_fs { "channels", 0U };
Readonly_value_file_system _format_fs { "format", 0U };
Readonly_value_file_system _sample_rate_fs { "sample_rate", 0U };
+ Value_file_system _ifrag_total_fs { "ifrag_total", 0U };
+ Value_file_system _ifrag_size_fs { "ifrag_size", 0U} ;
+ Readonly_value_file_system _ifrag_avail_fs { "ifrag_avail", 0U };
+ Readonly_value_file_system _ifrag_bytes_fs { "ifrag_bytes", 0U };
Value_file_system _ofrag_total_fs { "ofrag_total", 0U };
Value_file_system _ofrag_size_fs { "ofrag_size", 0U} ;
Readonly_value_file_system _ofrag_avail_fs { "ofrag_avail", 0U };
+ Readonly_value_file_system _ofrag_bytes_fs { "ofrag_bytes", 0U };
Readonly_value_file_system _optr_samples_fs { "optr_samples", 0LL };
Readonly_value_file_system _optr_fifo_samples_fs { "optr_fifo_samples", 0U };
Value_file_system _play_underruns_fs { "play_underruns", 0U };
+ /* WO files */
+ Value_file_system _halt_input_fs { "halt_input", 0U };
+ Value_file_system _halt_output_fs { "halt_output", 0U };
+
Audio::Info _info { _channels_fs, _format_fs, _sample_rate_fs,
- _ofrag_total_fs, _ofrag_size_fs, _ofrag_avail_fs,
+ _ifrag_total_fs, _ifrag_size_fs,
+ _ifrag_avail_fs, _ifrag_bytes_fs,
+ _ofrag_total_fs, _ofrag_size_fs,
+ _ofrag_avail_fs, _ofrag_bytes_fs,
_optr_samples_fs, _optr_fifo_samples_fs,
_play_underruns_fs };
- Readonly_value_file_system _info_fs { "info", _info };
+ Readonly_value_file_system _info_fs { "info", _info };
Audio _audio { _env.env(), _info, _info_fs };
+ Genode::Watch_handler _halt_input_handler {
+ _halt_input_fs, "/halt_input",
+ _env.alloc(),
+ *this,
+ &Vfs::Oss_file_system::Local_factory::_halt_input_changed };
+
+ Genode::Watch_handler _halt_output_handler {
+ _halt_output_fs, "/halt_output",
+ _env.alloc(),
+ *this,
+ &Vfs::Oss_file_system::Local_factory::_halt_output_changed };
+
+ Genode::Watch_handler _ifrag_total_handler {
+ _ifrag_total_fs, "/ifrag_total",
+ _env.alloc(),
+ *this,
+ &Vfs::Oss_file_system::Local_factory::_ifrag_total_changed };
+
+ Genode::Watch_handler _ifrag_size_handler {
+ _ifrag_size_fs, "/ifrag_size",
+ _env.alloc(),
+ *this,
+ &Vfs::Oss_file_system::Local_factory::_ofrag_size_changed };
+
Genode::Watch_handler _ofrag_total_handler {
_ofrag_total_fs, "/ofrag_total",
_env.alloc(),
@@ -558,17 +820,67 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
*this,
&Vfs::Oss_file_system::Local_factory::_play_underruns_changed };
- static constexpr size_t _native_stream_size { (Audio_out::QUEUE_SIZE - 2) *
- _stream_packet_size };
+ static constexpr size_t _ifrag_total_min { 2 };
+ static constexpr size_t _ifrag_size_min { _audio_in_stream_packet_size };
+ static constexpr size_t _ifrag_total_max { _audio_in_stream_size / _ifrag_size_min };
+ static constexpr size_t _ifrag_size_max { _audio_in_stream_size / _ifrag_total_min };
+
static constexpr size_t _ofrag_total_min { 2 };
- static constexpr size_t _ofrag_size_min { _stream_packet_size };
- static constexpr size_t _ofrag_total_max { _native_stream_size / _ofrag_size_min };
- static constexpr size_t _ofrag_size_max { _native_stream_size / _ofrag_total_min };
+ static constexpr size_t _ofrag_size_min { _audio_out_stream_packet_size };
+ static constexpr size_t _ofrag_total_max { _audio_out_stream_size / _ofrag_size_min };
+ static constexpr size_t _ofrag_size_max { _audio_out_stream_size / _ofrag_total_min };
/********************
** Watch handlers **
********************/
+ void _halt_input_changed()
+ {
+ _audio.halt_input();
+ }
+
+ void _halt_output_changed()
+ {
+ _audio.halt_output();
+ }
+
+ void _ifrag_total_changed()
+ {
+ unsigned ifrag_total_new = _ifrag_total_fs.value();
+
+ ifrag_total_new = Genode::max(ifrag_total_new, _ifrag_total_min);
+ ifrag_total_new = Genode::min(ifrag_total_new, _ifrag_total_max);
+
+ if (ifrag_total_new * _info.ifrag_size > _audio_in_stream_size)
+ _info.ifrag_size = 1 << Genode::log2(_audio_in_stream_size / ifrag_total_new);
+
+ _info.ifrag_total = ifrag_total_new;
+ _info.ifrag_avail = 0;
+ _info.ifrag_bytes = 0;
+
+ _info.update();
+ _info_fs.value(_info);
+ }
+
+ void _ifrag_size_changed()
+ {
+ unsigned ifrag_size_new = _ifrag_size_fs.value();
+
+ ifrag_size_new = Genode::max(ifrag_size_new, _ifrag_size_min);
+ ifrag_size_new = Genode::min(ifrag_size_new, _ifrag_size_max);
+
+ if (ifrag_size_new * _info.ifrag_total > _audio_in_stream_size) {
+ _info.ifrag_total = _audio_in_stream_size / ifrag_size_new;
+ _info.ifrag_avail = 0;
+ _info.ifrag_bytes = 0;
+ }
+
+ _info.ifrag_size = ifrag_size_new;
+
+ _info.update();
+ _info_fs.value(_info);
+ }
+
void _ofrag_total_changed()
{
unsigned ofrag_total_new = _ofrag_total_fs.value();
@@ -576,11 +888,12 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
ofrag_total_new = Genode::max(ofrag_total_new, _ofrag_total_min);
ofrag_total_new = Genode::min(ofrag_total_new, _ofrag_total_max);
- if (ofrag_total_new * _info.ofrag_size > _native_stream_size)
- _info.ofrag_size = 1 << Genode::log2(_native_stream_size / ofrag_total_new);
+ if (ofrag_total_new * _info.ofrag_size > _audio_out_stream_size)
+ _info.ofrag_size = 1 << Genode::log2(_audio_out_stream_size / ofrag_total_new);
_info.ofrag_total = ofrag_total_new;
_info.ofrag_avail = ofrag_total_new;
+ _info.ofrag_bytes = ofrag_total_new * _info.ofrag_size;
_info.update();
_info_fs.value(_info);
@@ -593,9 +906,10 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
ofrag_size_new = Genode::max(ofrag_size_new, _ofrag_size_min);
ofrag_size_new = Genode::min(ofrag_size_new, _ofrag_size_max);
- if (ofrag_size_new * _info.ofrag_total > _native_stream_size) {
- _info.ofrag_total = _native_stream_size / ofrag_size_new;
+ if (ofrag_size_new * _info.ofrag_total > _audio_out_stream_size) {
+ _info.ofrag_total = _audio_out_stream_size / ofrag_size_new;
_info.ofrag_avail = _info.ofrag_total;
+ _info.ofrag_bytes = _info.ofrag_total * _info.ofrag_size;
}
_info.ofrag_size = ofrag_size_new;
@@ -607,7 +921,6 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
void _play_underruns_changed()
{
/* reset counter */
-
_info.play_underruns = 0;
_info.update();
@@ -649,10 +962,22 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
return &_sample_rate_fs;
}
+ if (_ifrag_avail_fs.matches(node)) {
+ return &_ifrag_avail_fs;
+ }
+
+ if (_ifrag_bytes_fs.matches(node)) {
+ return &_ifrag_bytes_fs;
+ }
+
if (_ofrag_avail_fs.matches(node)) {
return &_ofrag_avail_fs;
}
+ if (_ofrag_bytes_fs.matches(node)) {
+ return &_ofrag_bytes_fs;
+ }
+
if (_format_fs.matches(node)) {
return &_format_fs;
}
@@ -668,6 +993,22 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
if (node.has_type(Value_file_system::type_name())) {
+ if (_halt_input_fs.matches(node)) {
+ return &_halt_input_fs;
+ }
+
+ if (_halt_output_fs.matches(node)) {
+ return &_halt_output_fs;
+ }
+
+ if (_ifrag_total_fs.matches(node)) {
+ return &_ifrag_total_fs;
+ }
+
+ if (_ifrag_size_fs.matches(node)) {
+ return &_ifrag_size_fs;
+ }
+
if (_ofrag_total_fs.matches(node)) {
return &_ofrag_total_fs;
}
@@ -693,7 +1034,7 @@ class Vfs::Oss_file_system::Compound_file_system : private Local_factory,
using Name = Oss_file_system::Name;
- using Config = String<512>;
+ using Config = String<1024>;
static Config _config(Name const &name)
{
char buf[Config::capacity()] { };
@@ -724,6 +1065,30 @@ class Vfs::Oss_file_system::Compound_file_system : private Local_factory,
xml.attribute("name", "format");
});
+ xml.node("value", [&] {
+ xml.attribute("name", "halt_input");
+ });
+
+ xml.node("value", [&] {
+ xml.attribute("name", "halt_output");
+ });
+
+ xml.node("value", [&] {
+ xml.attribute("name", "ifrag_total");
+ });
+
+ xml.node("value", [&] {
+ xml.attribute("name", "ifrag_size");
+ });
+
+ xml.node("readonly_value", [&] {
+ xml.attribute("name", "ifrag_avail");
+ });
+
+ xml.node("readonly_value", [&] {
+ xml.attribute("name", "ifrag_bytes");
+ });
+
xml.node("value", [&] {
xml.attribute("name", "ofrag_total");
});
@@ -736,6 +1101,10 @@ class Vfs::Oss_file_system::Compound_file_system : private Local_factory,
xml.attribute("name", "ofrag_avail");
});
+ xml.node("readonly_value", [&] {
+ xml.attribute("name", "ofrag_bytes");
+ });
+
xml.node("readonly_value", [&] {
xml.attribute("name", "optr_samples");
});
diff --git a/repos/libports/src/test/oss/main.cc b/repos/libports/src/test/oss/main.cc
new file mode 100644
index 0000000000..3e981479d3
--- /dev/null
+++ b/repos/libports/src/test/oss/main.cc
@@ -0,0 +1,50 @@
+/*
+ * \brief OSS test
+ * \author Christian Prochaska
+ * \date 2021-10-07
+ */
+
+/*
+ * Copyright (C) 2021-2022 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* libc includes */
+#include
+#include
+#include
+
+#include
+
+int main(int argc, char **argv)
+{
+ static char buf[2048];
+
+ int fd = open("/dev/dsp", O_RDWR);
+
+ if (fd < 0) {
+ printf("Error: could not open /dev/dsp\n");
+ return -1;
+ }
+
+ for (;;) {
+
+ ssize_t bytes_read = read(fd, buf, sizeof(buf));
+
+ if (bytes_read != sizeof(buf)) {
+ printf("Error: read error\n");
+ return -1;
+ }
+
+ ssize_t bytes_written = write(fd, buf, sizeof(buf));
+
+ if (bytes_written != sizeof(buf)) {
+ printf("Error: write error\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/repos/libports/src/test/oss/target.mk b/repos/libports/src/test/oss/target.mk
new file mode 100644
index 0000000000..fef2149dfd
--- /dev/null
+++ b/repos/libports/src/test/oss/target.mk
@@ -0,0 +1,5 @@
+TARGET = test-oss
+SRC_CC = main.cc
+LIBS = posix
+
+CC_CXX_WARN_STRICT =