From 33402e407fe468249fd9aa0a364d84d1b469dca6 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Tue, 11 Jan 2022 22:18:04 +0100 Subject: [PATCH] vfs_oss improvements Fixes #4375 --- repos/libports/recipes/src/vfs_oss/used_apis | 1 + repos/libports/run/oss.run | 98 +++ repos/libports/src/lib/libc/vfs_plugin.cc | 223 ++++-- repos/libports/src/lib/vfs/oss/README | 45 +- repos/libports/src/lib/vfs/oss/vfs_oss.cc | 671 ++++++++++++++----- repos/libports/src/test/oss/main.cc | 50 ++ repos/libports/src/test/oss/target.mk | 5 + 7 files changed, 879 insertions(+), 214 deletions(-) create mode 100644 repos/libports/run/oss.run create mode 100644 repos/libports/src/test/oss/main.cc create mode 100644 repos/libports/src/test/oss/target.mk 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 =