vfs_oss improvements

Fixes #4375
This commit is contained in:
Christian Prochaska 2022-01-11 22:18:04 +01:00 committed by Christian Helmuth
parent a1cac9b837
commit 33402e407f
7 changed files with 879 additions and 214 deletions

View File

@ -1,3 +1,4 @@
audio_in_session
audio_out_session audio_out_session
base base
gems gems

View File

@ -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 {
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="200"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Timer"/> </provides>
</start>
}
append_platform_drv_config
append config {
<start name="audio_drv">
<binary name="} [audio_drv_binary] {"/>
<resource name="RAM" quantum="8M"/>
<provides>
<service name="Audio_out"/>
<service name="Audio_in"/>
</provides>
<config verbose="yes">
<!-- tested on Lenovo x260 -->
<mixer field="outputs.master" value="128"/>
<mixer field="record.adc-0:1_source" value="mic"/>
<mixer field="record.adc-0:1" value="128"/>
<mixer field="record.enable" value="on"/>
</config>
</start>
<start name="test-oss">
<resource name="RAM" quantum="10M"/>
<config>
<vfs>
<dir name="dev">
<log/>
<oss name="dsp"/>
</dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
<route>
<service name="Audio_in"> <child name="audio_drv"/> </service>
<service name="Audio_out"> <child name="audio_drv"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</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

View File

@ -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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * 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 }; 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; unsigned int const ifrag_size =
abinfo = { info.attribute_value("ifrag_size", 0U);
.fragments = 4, unsigned int const ifrag_avail =
.fragstotal = 256, info.attribute_value("ifrag_avail", 0U);
.fragsize = 2048, unsigned int const ifrag_total =
.bytes = 4*2048, 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;
}
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; handled = true;
});
return Fn::COMPLETE;
});
} else if (request == SNDCTL_DSP_GETOPTR) { } 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); info.attribute_value("ofrag_avail", 0U);
unsigned int const ofrag_total = unsigned int const ofrag_total =
info.attribute_value("ofrag_total", 0U); info.attribute_value("ofrag_total", 0U);
unsigned int const ofrag_bytes =
info.attribute_value("ofrag_bytes", 0U);
if (!ofrag_size || !ofrag_total) { if (!ofrag_size || !ofrag_total) {
result = ENOTSUP; result = ENOTSUP;
return; 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 fragments = (int)ofrag_avail;
int const fragstotal = (int)ofrag_total; int const fragstotal = (int)ofrag_total;
int const fragsize = (int)ofrag_size; 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; result = EINVAL;
return; return;
} }
@ -1402,7 +1437,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
buf_info->fragments = fragments; buf_info->fragments = fragments;
buf_info->fragstotal = fragstotal; buf_info->fragstotal = fragstotal;
buf_info->fragsize = fragsize; buf_info->fragsize = fragsize;
buf_info->bytes = fragments * fragsize; buf_info->bytes = bytes;
handled = true; handled = true;
}); });
@ -1444,10 +1479,37 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
handled = true; 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; handled = true;
warning("SNDCTL_DSP_RESET handled=", handled, " result=", result);
} else if (request == SNDCTL_DSP_SAMPLESIZE) { } else if (request == SNDCTL_DSP_SAMPLESIZE) {
@ -1487,6 +1549,62 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
int max_fragments = *frag >> 16; int max_fragments = *frag >> 16;
int size_selector = *frag & ((1<<16) - 1); int size_selector = *frag & ((1<<16) - 1);
if (((fd->flags & O_ACCMODE) == O_RDONLY) ||
((fd->flags & O_ACCMODE) == O_RDWR)) {
char ifrag_total_string[16];
char ifrag_size_string[16];
::snprintf(ifrag_total_string, sizeof(ifrag_total_string),
"%u", max_fragments);
::snprintf(ifrag_size_string, sizeof(ifrag_size_string),
"%u", 1 << size_selector);
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);
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);
monitor().monitor([&] {
_with_info(*fd, [&] (Xml_node info) {
if (info.type() != "oss") {
return;
}
unsigned int const ifrag_size =
info.attribute_value("ifrag_size", 0U);
unsigned int const ifrag_size_log2 =
ifrag_size ? Genode::log2(ifrag_size) : 0;
unsigned int const ifrag_total =
info.attribute_value("ifrag_total", 0U);
if (!ifrag_total || !ifrag_size_log2) {
result = ENOTSUP;
return;
}
});
return Fn::COMPLETE;
});
}
if (((fd->flags & O_ACCMODE) == O_WRONLY) ||
((fd->flags & O_ACCMODE) == O_RDWR)) {
char ofrag_total_string[16]; char ofrag_total_string[16];
char ofrag_size_string[16]; char ofrag_size_string[16];
@ -1535,6 +1653,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
return Fn::COMPLETE; return Fn::COMPLETE;
}); });
}
handled = true; 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 * Either handled or a failed attempt will mark the I/O control
* as handled. * as handled.
*/ */
if (request == SNDCTL_DSP_RESET)
warning("SNDCTL_DSP_RESET handled=", handled, " result=", result);
return { handled || result != 0, result }; return { handled || result != 0, result };
} }

View File

@ -1,16 +1,17 @@
The VFS OSS plugin offers access to Genode's Audio_out session by providing a The VFS OSS plugin offers access to Genode's Audio_out and Audio_in sessions by
file-system that can be mounted at arbitrary location within the VFS of a providing a file-system that can be mounted at arbitrary location within the VFS
component. It exposes a data file that can be used as 'dsp' file, e.g., _/dev/dsp_ of a component. It exposes a data file that can be used as 'dsp' file, e.g.,
as is common with OSS. The support I/O control operations or rather the _/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' 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_. 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 This file may by used to query the configured parameters and has the following
structure: structure:
! <oss channels="2" sample_rate="44000" format="16" ! <oss channels="2" format="16" sample_rate="44100" ifrag_total="430"
! ofrag_total="256" ofrag_size="2048" ofrag_avail="256" ! ifrag_size="2048" ifrag_avail="0" ifrag_bytes="0" ofrag_total="254"
| optr_samples="0" optr_fifo_samples="0" play_underruns="0"/> ! ofrag_size="2048" ofrag_avail="254" ofrag_bytes="520192" optr_samples="0"
! optr_fifo_samples="0" play_underruns="0"/>
Each parameter can also be accessed via its own file. The following list Each parameter can also be accessed via its own file. The following list
presents all files: presents all files:
@ -18,11 +19,25 @@ presents all files:
* :channels (ro): number of available channels. Set to 2 (stereo). * :channels (ro): number of available channels. Set to 2 (stereo).
Corresponding OSS commands: 'SNDCTL_DSP_CHANNELS' 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. * :sample_rate (ro): sample rate of the underlying Audio_out session.
Corresponding OSS commands: 'SNDCTL_DSP_SPEED' Corresponding OSS commands: 'SNDCTL_DSP_SPEED'
* :format (ro): sample format, e.g. s16le. Defaults to AFMT_S16_LE. * :ifrag_total (rw): total number of input fragments. Set to number of packets
Corresponding OSS commands: 'SNDCTL_DSP_SAMPLESIZE' 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 * :ofrag_total (rw): total number of output fragments. Set to number of packets
in the underlying Audio_out session's packet-stream by default. in the underlying Audio_out session's packet-stream by default.
@ -36,6 +51,10 @@ presents all files:
fragment count. fragment count.
Corresponding OSS commands: 'SNDCTL_DSP_GETOSPACE' 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 * :optr_samples (ro): total number of samples submitted to the Audio_out session
Corresponding OSS commands: 'SNDCTL_DSP_CURRENT_OPTR' Corresponding OSS commands: 'SNDCTL_DSP_CURRENT_OPTR'
@ -46,7 +65,13 @@ presents all files:
into this file resets the value to zero into this file resets the value to zero
Corresponding OSS commands: 'SNDCTL_DSP_GETERROR' 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 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. of all parameters and will only work when 44100Hz/s16le is used.

View File

@ -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 * \author Josef Soentgen
* \date 2018-10-25 * \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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
*/ */
/* Genode includes */ /* Genode includes */
#include <audio_in_session/connection.h>
#include <audio_out_session/connection.h> #include <audio_out_session/connection.h>
#include <base/registry.h> #include <base/registry.h>
#include <base/signal.h> #include <base/signal.h>
@ -25,9 +26,28 @@
/* libc includes */ /* libc includes */
#include <sys/soundcard.h> #include <sys/soundcard.h>
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; } namespace Vfs { struct Oss_file_system; }
@ -53,9 +73,14 @@ struct Vfs::Oss_file_system::Audio
unsigned channels; unsigned channels;
unsigned format; unsigned format;
unsigned sample_rate; unsigned sample_rate;
unsigned ifrag_total;
unsigned ifrag_size;
unsigned ifrag_avail;
unsigned ifrag_bytes;
unsigned ofrag_total; unsigned ofrag_total;
unsigned ofrag_size; unsigned ofrag_size;
unsigned ofrag_avail; unsigned ofrag_avail;
unsigned ofrag_bytes;
long long optr_samples; long long optr_samples;
unsigned optr_fifo_samples; unsigned optr_fifo_samples;
unsigned play_underruns; unsigned play_underruns;
@ -63,9 +88,14 @@ struct Vfs::Oss_file_system::Audio
Readonly_value_file_system<unsigned> &_channels_fs; Readonly_value_file_system<unsigned> &_channels_fs;
Readonly_value_file_system<unsigned> &_format_fs; Readonly_value_file_system<unsigned> &_format_fs;
Readonly_value_file_system<unsigned> &_sample_rate_fs; Readonly_value_file_system<unsigned> &_sample_rate_fs;
Value_file_system<unsigned> &_ifrag_total_fs;
Value_file_system<unsigned> &_ifrag_size_fs;
Readonly_value_file_system<unsigned> &_ifrag_avail_fs;
Readonly_value_file_system<unsigned> &_ifrag_bytes_fs;
Value_file_system<unsigned> &_ofrag_total_fs; Value_file_system<unsigned> &_ofrag_total_fs;
Value_file_system<unsigned> &_ofrag_size_fs; Value_file_system<unsigned> &_ofrag_size_fs;
Readonly_value_file_system<unsigned> &_ofrag_avail_fs; Readonly_value_file_system<unsigned> &_ofrag_avail_fs;
Readonly_value_file_system<unsigned> &_ofrag_bytes_fs;
Readonly_value_file_system<long long> &_optr_samples_fs; Readonly_value_file_system<long long> &_optr_samples_fs;
Readonly_value_file_system<unsigned> &_optr_fifo_samples_fs; Readonly_value_file_system<unsigned> &_optr_fifo_samples_fs;
Value_file_system<unsigned> &_play_underruns_fs; Value_file_system<unsigned> &_play_underruns_fs;
@ -73,9 +103,14 @@ struct Vfs::Oss_file_system::Audio
Info(Readonly_value_file_system<unsigned> &channels_fs, Info(Readonly_value_file_system<unsigned> &channels_fs,
Readonly_value_file_system<unsigned> &format_fs, Readonly_value_file_system<unsigned> &format_fs,
Readonly_value_file_system<unsigned> &sample_rate_fs, Readonly_value_file_system<unsigned> &sample_rate_fs,
Value_file_system<unsigned> &ifrag_total_fs,
Value_file_system<unsigned> &ifrag_size_fs,
Readonly_value_file_system<unsigned> &ifrag_avail_fs,
Readonly_value_file_system<unsigned> &ifrag_bytes_fs,
Value_file_system<unsigned> &ofrag_total_fs, Value_file_system<unsigned> &ofrag_total_fs,
Value_file_system<unsigned> &ofrag_size_fs, Value_file_system<unsigned> &ofrag_size_fs,
Readonly_value_file_system<unsigned> &ofrag_avail_fs, Readonly_value_file_system<unsigned> &ofrag_avail_fs,
Readonly_value_file_system<unsigned> &ofrag_bytes_fs,
Readonly_value_file_system<long long> &optr_samples_fs, Readonly_value_file_system<long long> &optr_samples_fs,
Readonly_value_file_system<unsigned> &optr_fifo_samples_fs, Readonly_value_file_system<unsigned> &optr_fifo_samples_fs,
Value_file_system<unsigned> &play_underruns_fs) Value_file_system<unsigned> &play_underruns_fs)
@ -83,18 +118,28 @@ struct Vfs::Oss_file_system::Audio
channels { 0 }, channels { 0 },
format { 0 }, format { 0 },
sample_rate { 0 }, sample_rate { 0 },
ifrag_total { 0 },
ifrag_size { 0 },
ifrag_avail { 0 },
ifrag_bytes { 0 },
ofrag_total { 0 }, ofrag_total { 0 },
ofrag_size { 0 }, ofrag_size { 0 },
ofrag_avail { 0 }, ofrag_avail { 0 },
ofrag_bytes { 0 },
optr_samples { 0 }, optr_samples { 0 },
optr_fifo_samples { 0 }, optr_fifo_samples { 0 },
play_underruns { 0 }, play_underruns { 0 },
_channels_fs { channels_fs }, _channels_fs { channels_fs },
_format_fs { format_fs }, _format_fs { format_fs },
_sample_rate_fs { sample_rate_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_total_fs { ofrag_total_fs },
_ofrag_size_fs { ofrag_size_fs }, _ofrag_size_fs { ofrag_size_fs },
_ofrag_avail_fs { ofrag_avail_fs }, _ofrag_avail_fs { ofrag_avail_fs },
_ofrag_bytes_fs { ofrag_bytes_fs },
_optr_samples_fs { optr_samples_fs }, _optr_samples_fs { optr_samples_fs },
_optr_fifo_samples_fs { optr_fifo_samples_fs }, _optr_fifo_samples_fs { optr_fifo_samples_fs },
_play_underruns_fs { play_underruns_fs } _play_underruns_fs { play_underruns_fs }
@ -105,9 +150,14 @@ struct Vfs::Oss_file_system::Audio
_channels_fs .value(channels); _channels_fs .value(channels);
_format_fs .value(format); _format_fs .value(format);
_sample_rate_fs .value(sample_rate); _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_total_fs .value(ofrag_total);
_ofrag_size_fs .value(ofrag_size); _ofrag_size_fs .value(ofrag_size);
_ofrag_avail_fs .value(ofrag_avail); _ofrag_avail_fs .value(ofrag_avail);
_ofrag_bytes_fs .value(ofrag_bytes);
_optr_samples_fs .value(optr_samples); _optr_samples_fs .value(optr_samples);
_optr_fifo_samples_fs.value(optr_fifo_samples); _optr_fifo_samples_fs.value(optr_fifo_samples);
_play_underruns_fs .value(play_underruns); _play_underruns_fs .value(play_underruns);
@ -115,15 +165,20 @@ struct Vfs::Oss_file_system::Audio
void print(Genode::Output &out) const void print(Genode::Output &out) const
{ {
char buf[256] { }; char buf[512] { };
Genode::Xml_generator xml(buf, sizeof(buf), "oss", [&] () { Genode::Xml_generator xml(buf, sizeof(buf), "oss", [&] () {
xml.attribute("channels", channels); xml.attribute("channels", channels);
xml.attribute("format", format); xml.attribute("format", format);
xml.attribute("sample_rate", sample_rate); 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_total", ofrag_total);
xml.attribute("ofrag_size", ofrag_size); xml.attribute("ofrag_size", ofrag_size);
xml.attribute("ofrag_avail", ofrag_avail); xml.attribute("ofrag_avail", ofrag_avail);
xml.attribute("ofrag_bytes", ofrag_bytes);
xml.attribute("optr_samples", optr_samples); xml.attribute("optr_samples", optr_samples);
xml.attribute("optr_fifo_samples", optr_fifo_samples); xml.attribute("optr_fifo_samples", optr_fifo_samples);
xml.attribute("play_underruns", play_underruns); xml.attribute("play_underruns", play_underruns);
@ -138,21 +193,26 @@ struct Vfs::Oss_file_system::Audio
Audio(Audio const &); Audio(Audio const &);
Audio &operator = (Audio const &); Audio &operator = (Audio const &);
bool _started { false }; bool _audio_out_started { false };
bool _audio_in_started { false };
enum { CHANNELS = 2, }; enum { CHANNELS = 2, };
const char *_channel_names[CHANNELS] = { "front left", "front right" }; const char *_channel_names[CHANNELS] = { "front left", "front right" };
Genode::Constructible<Audio_out::Connection> _out[CHANNELS]; Genode::Constructible<Audio_out::Connection> _out[CHANNELS];
Genode::Constructible<Audio_in::Connection> _in { };
Info &_info; Info &_info;
Readonly_value_file_system<Info, 256> &_info_fs; Readonly_value_file_system<Info, 512> &_info_fs;
size_t _read_sample_offset { 0 };
size_t _write_sample_offset { 0 };
public: public:
Audio(Genode::Env &env, Audio(Genode::Env &env,
Info &info, Info &info,
Readonly_value_file_system<Info, 256> &info_fs) Readonly_value_file_system<Info, 512> &info_fs)
: :
_info { info }, _info { info },
_info_fs { info_fs } _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.channels = CHANNELS;
_info.format = (unsigned)AFMT_S16_LE; _info.format = (unsigned)AFMT_S16_LE;
_info.sample_rate = Audio_out::SAMPLE_RATE; _info.sample_rate = Audio_out::SAMPLE_RATE;
_info.ofrag_total = Audio_out::QUEUE_SIZE - 2; _info.ifrag_size = 2048;
_info.ofrag_size = _info.ifrag_total = _audio_in_stream_size / _info.ifrag_size;
(unsigned)Audio_out::PERIOD * (unsigned)CHANNELS _info.ifrag_avail = 0;
* sizeof (int16_t); _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_avail = _info.ofrag_total;
_info.ofrag_bytes = _info.ofrag_avail * _info.ofrag_size;
_info.update(); _info.update();
_info_fs.value(_info); _info_fs.value(_info);
} }
void alloc_sigh(Genode::Signal_context_capability sigh) void out_progress_sigh(Genode::Signal_context_capability sigh)
{
_out[0]->alloc_sigh(sigh);
}
void progress_sigh(Genode::Signal_context_capability sigh)
{ {
_out[0]->progress_sigh(sigh); _out[0]->progress_sigh(sigh);
} }
void pause() void in_progress_sigh(Genode::Signal_context_capability sigh)
{ {
for (int i = 0; i < CHANNELS; i++) { _in->progress_sigh(sigh);
_out[i]->stop();
} }
_started = false; void in_overrun_sigh(Genode::Signal_context_capability sigh)
{
_in->overrun_sigh(sigh);
} }
unsigned queued() const bool read_ready()
{ {
return _out[0]->stream()->queued(); return _info.ifrag_bytes > 0;
} }
void update_info_ofrag_avail_from_optr_fifo_samples() void update_info_ofrag_avail_from_optr_fifo_samples()
{ {
unsigned const samples_per_fragment = _info.ofrag_bytes = (_info.ofrag_total * _info.ofrag_size) -
_info.ofrag_size / (CHANNELS * sizeof(int16_t)); ((_info.optr_fifo_samples + _write_sample_offset) *
_info.ofrag_avail = _info.ofrag_total - CHANNELS * sizeof(int16_t));
((_info.optr_fifo_samples / samples_per_fragment) + _info.ofrag_avail = _info.ofrag_bytes / _info.ofrag_size;
((_info.optr_fifo_samples % samples_per_fragment) ? 1 : 0));
_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. * 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) { 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 * The queue count can wrap from 0 to 255 if packets are not
* submitted fast enough. * submitted fast enough.
*/ */
if ((fifo_samples_new == 0) || if ((fifo_samples_new == 0) ||
(fifo_samples_new > _info.optr_fifo_samples)) { (fifo_samples_new > _info.optr_fifo_samples)) {
pause();
halt_output();
_write_sample_offset = 0;
if (fifo_samples_new > _info.optr_fifo_samples) { if (fifo_samples_new > _info.optr_fifo_samples) {
_info.play_underruns++; _info.play_underruns++;
fifo_samples_new = 0; 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; _info.optr_fifo_samples = fifo_samples_new;
update_info_ofrag_avail_from_optr_fifo_samples(); 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.update();
_info_fs.value(_info); _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; return true;
} }
@ -254,122 +474,106 @@ struct Vfs::Oss_file_system::Audio
{ {
using namespace Genode; using namespace Genode;
out_size = 0;
if (_info.ofrag_bytes == 0)
throw Vfs::File_io_service::Insufficient_buffer();
bool block_write = false; bool block_write = false;
/* if (buf_size > _info.ofrag_bytes) {
* Calculate how many strean packets would be needed to buf_size = _info.ofrag_bytes;
* 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;
block_write = true; block_write = true;
} }
if (stream_packets_to_write == 0) { unsigned stream_samples_to_write = buf_size / CHANNELS / sizeof(int16_t);
out_size = 0;
throw Vfs::File_io_service::Insufficient_buffer(); if (stream_samples_to_write == 0) {
/* invalid argument */
return false;
} }
if (!_started) { if (!_audio_out_started) {
_started = true; _audio_out_started = true;
_out[0]->start(); _out[0]->start();
_out[1]->start(); _out[1]->start();
} }
for (unsigned packet_count = 0; unsigned stream_samples_written = 0;
packet_count < stream_packets_to_write;
packet_count++) { /* packet loop */
for (;;) {
Audio_out::Packet *lp = nullptr; Audio_out::Packet *lp = nullptr;
try { lp = _out[0]->stream()->alloc(); } if (_write_sample_offset == 0) {
catch (...) {
error("stream full", for (;;) {
" queued: ", _out[0]->stream()->queued(), try {
" pos: ", _out[0]->stream()->pos(), lp = _out[0]->stream()->alloc();
" tail: ", _out[0]->stream()->tail()
);
break; 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); unsigned const pos = _out[0]->stream()->packet_position(lp);
Audio_out::Packet *rp = _out[1]->stream()->get(pos); Audio_out::Packet *rp = _out[1]->stream()->get(pos);
float *dest[CHANNELS] = { lp->content(), rp->content() }; float *dest[CHANNELS] = { lp->content(), rp->content() };
for (unsigned sample_count = 0; /* sample loop */
sample_count < Audio_out::PERIOD;
sample_count++) { for (;;) {
for (unsigned c = 0; c < CHANNELS; c++) { for (unsigned c = 0; c < CHANNELS; c++) {
unsigned const buf_index = out_size / sizeof(int16_t);
unsigned const buf_index = int16_t src_sample = ((int16_t const*)buf)[buf_index];
(packet_count * Audio_out::PERIOD * CHANNELS) + dest[c][_write_sample_offset] = ((float)src_sample) / 32768.0f;
(sample_count * CHANNELS) + c; out_size += sizeof(int16_t);
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;
} }
dest[c][sample_count] = ((float)src_sample) / 32768.0f; stream_samples_written++;
}
}
_write_sample_offset++;
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[0]->submit(lp);
_out[1]->submit(rp); _out[1]->submit(rp);
_write_sample_offset = 0;
if (stream_samples_written != stream_samples_to_write)
break;
} }
out_size = Genode::min(stream_packets_to_write * if (stream_samples_written == stream_samples_to_write) {
_stream_packet_size, buf_size);
/* update info */ /* 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(); update_info_ofrag_avail_from_optr_fifo_samples();
_info.update();
_info_fs.value(_info);
if (block_write) { throw Vfs::File_io_service::Insufficient_buffer(); } if (block_write) { throw Vfs::File_io_service::Insufficient_buffer(); }
return true;
}
}
}
return true; return true;
} }
}; };
@ -403,16 +607,26 @@ 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 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;
out_count = buf_size;
if (buf_size == 0) {
out_count = 0;
return READ_OK; return READ_OK;
} }
bool success = _audio.read(buf, buf_size, out_count);
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, Write_result write(char const *buf, file_size buf_size,
file_size &out_count) override file_size &out_count) override
{ {
@ -426,7 +640,7 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
bool read_ready() override 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 { }; Handle_registry _handle_registry { };
Genode::Io_signal_handler<Vfs::Oss_file_system::Data_file_system> _alloc_avail_sigh { Genode::Io_signal_handler<Vfs::Oss_file_system::Data_file_system> _audio_out_progress_sigh {
_ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_alloc_avail }; _ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_audio_out_progress };
void _handle_alloc_avail() { } Genode::Io_signal_handler<Vfs::Oss_file_system::Data_file_system> _audio_in_progress_sigh {
_ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_audio_in_progress };
Genode::Io_signal_handler<Vfs::Oss_file_system::Data_file_system> _progress_sigh { void _handle_audio_out_progress()
_ep, *this, &Vfs::Oss_file_system::Data_file_system::_handle_progress };
void _handle_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 */ /* at least one stream packet is available */
_handle_registry.for_each([this] (Registered_handle &handle) { _handle_registry.for_each([this] (Registered_handle &handle) {
if (handle.blocked) { if (handle.blocked) {
@ -468,8 +693,8 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
_ep { ep }, _ep { ep },
_audio { audio } _audio { audio }
{ {
_audio.alloc_sigh(_alloc_avail_sigh); _audio.out_progress_sigh(_audio_out_progress_sigh);
_audio.progress_sigh(_progress_sigh); _audio.in_progress_sigh(_audio_in_progress_sigh);
} }
static const char *name() { return "data"; } static const char *name() { return "data"; }
@ -521,25 +746,62 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
Vfs::Env &_env; Vfs::Env &_env;
/* RO/RW files */
Readonly_value_file_system<unsigned> _channels_fs { "channels", 0U }; Readonly_value_file_system<unsigned> _channels_fs { "channels", 0U };
Readonly_value_file_system<unsigned> _format_fs { "format", 0U }; Readonly_value_file_system<unsigned> _format_fs { "format", 0U };
Readonly_value_file_system<unsigned> _sample_rate_fs { "sample_rate", 0U }; Readonly_value_file_system<unsigned> _sample_rate_fs { "sample_rate", 0U };
Value_file_system<unsigned> _ifrag_total_fs { "ifrag_total", 0U };
Value_file_system<unsigned> _ifrag_size_fs { "ifrag_size", 0U} ;
Readonly_value_file_system<unsigned> _ifrag_avail_fs { "ifrag_avail", 0U };
Readonly_value_file_system<unsigned> _ifrag_bytes_fs { "ifrag_bytes", 0U };
Value_file_system<unsigned> _ofrag_total_fs { "ofrag_total", 0U }; Value_file_system<unsigned> _ofrag_total_fs { "ofrag_total", 0U };
Value_file_system<unsigned> _ofrag_size_fs { "ofrag_size", 0U} ; Value_file_system<unsigned> _ofrag_size_fs { "ofrag_size", 0U} ;
Readonly_value_file_system<unsigned> _ofrag_avail_fs { "ofrag_avail", 0U }; Readonly_value_file_system<unsigned> _ofrag_avail_fs { "ofrag_avail", 0U };
Readonly_value_file_system<unsigned> _ofrag_bytes_fs { "ofrag_bytes", 0U };
Readonly_value_file_system<long long> _optr_samples_fs { "optr_samples", 0LL }; Readonly_value_file_system<long long> _optr_samples_fs { "optr_samples", 0LL };
Readonly_value_file_system<unsigned> _optr_fifo_samples_fs { "optr_fifo_samples", 0U }; Readonly_value_file_system<unsigned> _optr_fifo_samples_fs { "optr_fifo_samples", 0U };
Value_file_system<unsigned> _play_underruns_fs { "play_underruns", 0U }; Value_file_system<unsigned> _play_underruns_fs { "play_underruns", 0U };
/* WO files */
Value_file_system<unsigned> _halt_input_fs { "halt_input", 0U };
Value_file_system<unsigned> _halt_output_fs { "halt_output", 0U };
Audio::Info _info { _channels_fs, _format_fs, _sample_rate_fs, 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, _optr_samples_fs, _optr_fifo_samples_fs,
_play_underruns_fs }; _play_underruns_fs };
Readonly_value_file_system<Audio::Info, 256> _info_fs { "info", _info }; Readonly_value_file_system<Audio::Info, 512> _info_fs { "info", _info };
Audio _audio { _env.env(), _info, _info_fs }; Audio _audio { _env.env(), _info, _info_fs };
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _halt_input_handler {
_halt_input_fs, "/halt_input",
_env.alloc(),
*this,
&Vfs::Oss_file_system::Local_factory::_halt_input_changed };
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _halt_output_handler {
_halt_output_fs, "/halt_output",
_env.alloc(),
*this,
&Vfs::Oss_file_system::Local_factory::_halt_output_changed };
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _ifrag_total_handler {
_ifrag_total_fs, "/ifrag_total",
_env.alloc(),
*this,
&Vfs::Oss_file_system::Local_factory::_ifrag_total_changed };
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _ifrag_size_handler {
_ifrag_size_fs, "/ifrag_size",
_env.alloc(),
*this,
&Vfs::Oss_file_system::Local_factory::_ofrag_size_changed };
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _ofrag_total_handler { Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _ofrag_total_handler {
_ofrag_total_fs, "/ofrag_total", _ofrag_total_fs, "/ofrag_total",
_env.alloc(), _env.alloc(),
@ -558,17 +820,67 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
*this, *this,
&Vfs::Oss_file_system::Local_factory::_play_underruns_changed }; &Vfs::Oss_file_system::Local_factory::_play_underruns_changed };
static constexpr size_t _native_stream_size { (Audio_out::QUEUE_SIZE - 2) * static constexpr size_t _ifrag_total_min { 2 };
_stream_packet_size }; 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_total_min { 2 };
static constexpr size_t _ofrag_size_min { _stream_packet_size }; static constexpr size_t _ofrag_size_min { _audio_out_stream_packet_size };
static constexpr size_t _ofrag_total_max { _native_stream_size / _ofrag_size_min }; static constexpr size_t _ofrag_total_max { _audio_out_stream_size / _ofrag_size_min };
static constexpr size_t _ofrag_size_max { _native_stream_size / _ofrag_total_min }; static constexpr size_t _ofrag_size_max { _audio_out_stream_size / _ofrag_total_min };
/******************** /********************
** Watch handlers ** ** 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() void _ofrag_total_changed()
{ {
unsigned ofrag_total_new = _ofrag_total_fs.value(); 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::max(ofrag_total_new, _ofrag_total_min);
ofrag_total_new = Genode::min(ofrag_total_new, _ofrag_total_max); ofrag_total_new = Genode::min(ofrag_total_new, _ofrag_total_max);
if (ofrag_total_new * _info.ofrag_size > _native_stream_size) if (ofrag_total_new * _info.ofrag_size > _audio_out_stream_size)
_info.ofrag_size = 1 << Genode::log2(_native_stream_size / ofrag_total_new); _info.ofrag_size = 1 << Genode::log2(_audio_out_stream_size / ofrag_total_new);
_info.ofrag_total = ofrag_total_new; _info.ofrag_total = ofrag_total_new;
_info.ofrag_avail = ofrag_total_new; _info.ofrag_avail = ofrag_total_new;
_info.ofrag_bytes = ofrag_total_new * _info.ofrag_size;
_info.update(); _info.update();
_info_fs.value(_info); _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::max(ofrag_size_new, _ofrag_size_min);
ofrag_size_new = Genode::min(ofrag_size_new, _ofrag_size_max); ofrag_size_new = Genode::min(ofrag_size_new, _ofrag_size_max);
if (ofrag_size_new * _info.ofrag_total > _native_stream_size) { if (ofrag_size_new * _info.ofrag_total > _audio_out_stream_size) {
_info.ofrag_total = _native_stream_size / ofrag_size_new; _info.ofrag_total = _audio_out_stream_size / ofrag_size_new;
_info.ofrag_avail = _info.ofrag_total; _info.ofrag_avail = _info.ofrag_total;
_info.ofrag_bytes = _info.ofrag_total * _info.ofrag_size;
} }
_info.ofrag_size = ofrag_size_new; _info.ofrag_size = ofrag_size_new;
@ -607,7 +921,6 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
void _play_underruns_changed() void _play_underruns_changed()
{ {
/* reset counter */ /* reset counter */
_info.play_underruns = 0; _info.play_underruns = 0;
_info.update(); _info.update();
@ -649,10 +962,22 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
return &_sample_rate_fs; 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)) { if (_ofrag_avail_fs.matches(node)) {
return &_ofrag_avail_fs; return &_ofrag_avail_fs;
} }
if (_ofrag_bytes_fs.matches(node)) {
return &_ofrag_bytes_fs;
}
if (_format_fs.matches(node)) { if (_format_fs.matches(node)) {
return &_format_fs; return &_format_fs;
} }
@ -668,6 +993,22 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
if (node.has_type(Value_file_system<unsigned>::type_name())) { if (node.has_type(Value_file_system<unsigned>::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)) { if (_ofrag_total_fs.matches(node)) {
return &_ofrag_total_fs; 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 Name = Oss_file_system::Name;
using Config = String<512>; using Config = String<1024>;
static Config _config(Name const &name) static Config _config(Name const &name)
{ {
char buf[Config::capacity()] { }; char buf[Config::capacity()] { };
@ -724,6 +1065,30 @@ class Vfs::Oss_file_system::Compound_file_system : private Local_factory,
xml.attribute("name", "format"); 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.node("value", [&] {
xml.attribute("name", "ofrag_total"); 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.attribute("name", "ofrag_avail");
}); });
xml.node("readonly_value", [&] {
xml.attribute("name", "ofrag_bytes");
});
xml.node("readonly_value", [&] { xml.node("readonly_value", [&] {
xml.attribute("name", "optr_samples"); xml.attribute("name", "optr_samples");
}); });

View File

@ -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 <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/soundcard.h>
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;
}

View File

@ -0,0 +1,5 @@
TARGET = test-oss
SRC_CC = main.cc
LIBS = posix
CC_CXX_WARN_STRICT =