mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-06 01:11:46 +00:00
parent
a6fb61dbf2
commit
149bd999f3
@ -14,6 +14,7 @@
|
|||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <vfs/dir_file_system.h>
|
#include <vfs/dir_file_system.h>
|
||||||
#include <vfs/single_file_system.h>
|
#include <vfs/single_file_system.h>
|
||||||
|
#include <vfs/value_file_system.h>
|
||||||
|
|
||||||
#include <os/vfs.h>
|
#include <os/vfs.h>
|
||||||
#include <util/xml_generator.h>
|
#include <util/xml_generator.h>
|
||||||
@ -22,7 +23,6 @@
|
|||||||
|
|
||||||
#include "directory_tree.h"
|
#include "directory_tree.h"
|
||||||
#include "trace_buffer.h"
|
#include "trace_buffer.h"
|
||||||
#include "value_file_system.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Vfs_trace {
|
namespace Vfs_trace {
|
||||||
|
@ -1188,7 +1188,13 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
*/
|
*/
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
if (request == SNDCTL_DSP_CHANNELS) {
|
if (request == OSS_GETVERSION) {
|
||||||
|
|
||||||
|
*(int *)argp = SOUND_VERSION;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_CHANNELS) {
|
||||||
|
|
||||||
monitor().monitor([&] {
|
monitor().monitor([&] {
|
||||||
_with_info(*fd, [&] (Xml_node info) {
|
_with_info(*fd, [&] (Xml_node info) {
|
||||||
@ -1203,7 +1209,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int const num_chans = *(int const*)argp;
|
int const num_chans = *(int const *)argp;
|
||||||
if (num_chans < 0) {
|
if (num_chans < 0) {
|
||||||
result = EINVAL;
|
result = EINVAL;
|
||||||
return;
|
return;
|
||||||
@ -1214,7 +1220,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(int*)argp = avail_chans;
|
*(int *)argp = avail_chans;
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
});
|
});
|
||||||
@ -1222,6 +1228,108 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return Fn::COMPLETE;
|
return Fn::COMPLETE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_CURRENT_OPTR) {
|
||||||
|
|
||||||
|
monitor().monitor([&] {
|
||||||
|
_with_info(*fd, [&] (Xml_node info) {
|
||||||
|
|
||||||
|
if (info.type() != "oss") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long const optr_samples =
|
||||||
|
info.attribute_value("optr_samples", -1L);
|
||||||
|
int const optr_fifo_samples =
|
||||||
|
info.attribute_value("optr_fifo_samples", -1L);
|
||||||
|
if ((optr_samples == -1) || (optr_fifo_samples == -1)) {
|
||||||
|
result = ENOTSUP;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oss_count_t *optr = (oss_count_t *)argp;
|
||||||
|
optr->samples = optr_samples;
|
||||||
|
optr->fifo_samples = optr_fifo_samples;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Fn::COMPLETE;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_GETERROR) {
|
||||||
|
|
||||||
|
int play_underruns = 0;
|
||||||
|
|
||||||
|
monitor().monitor([&] {
|
||||||
|
_with_info(*fd, [&] (Xml_node info) {
|
||||||
|
if (info.type() != "oss") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
play_underruns = info.attribute_value("play_underruns", 0U);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Fn::COMPLETE;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (play_underruns > 0) {
|
||||||
|
|
||||||
|
/* reset */
|
||||||
|
|
||||||
|
char const play_underruns_string[] = "0";
|
||||||
|
Absolute_path play_underruns_path = ioctl_dir(*fd);
|
||||||
|
play_underruns_path.append_element("play_underruns");
|
||||||
|
File_descriptor *play_underruns_fd = open(play_underruns_path.base(), O_RDWR);
|
||||||
|
if (!play_underruns_fd)
|
||||||
|
return { true, ENOTSUP };
|
||||||
|
write(play_underruns_fd, play_underruns_string, sizeof(play_underruns_string));
|
||||||
|
close(play_underruns_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct audio_errinfo *err_info =
|
||||||
|
(struct audio_errinfo *)argp;
|
||||||
|
|
||||||
|
err_info->play_underruns = play_underruns;
|
||||||
|
err_info->rec_overruns = 0;
|
||||||
|
err_info->play_ptradjust = 0;
|
||||||
|
err_info->rec_ptradjust = 0;
|
||||||
|
err_info->play_errorcount = 0;
|
||||||
|
err_info->rec_errorcount = 0;
|
||||||
|
err_info->play_lasterror = 0;
|
||||||
|
err_info->rec_lasterror = 0;
|
||||||
|
err_info->play_errorparm = 0;
|
||||||
|
err_info->rec_errorparm = 0;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_GETISPACE) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
audio_buf_info &abinfo = *(audio_buf_info *)argp;
|
||||||
|
abinfo = {
|
||||||
|
.fragments = 4,
|
||||||
|
.fragstotal = 256,
|
||||||
|
.fragsize = 2048,
|
||||||
|
.bytes = 4*2048,
|
||||||
|
};
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_GETOPTR) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
count_info &ci = *(count_info *)argp;
|
||||||
|
ci = {
|
||||||
|
.bytes = 0, /* Total # of bytes processed */
|
||||||
|
.blocks = 0, /* # of fragment transitions since last time */
|
||||||
|
.ptr = 0, /* Current DMA pointer value */
|
||||||
|
};
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
} else if (request == SNDCTL_DSP_GETOSPACE) {
|
} else if (request == SNDCTL_DSP_GETOSPACE) {
|
||||||
|
|
||||||
monitor().monitor([&] {
|
monitor().monitor([&] {
|
||||||
@ -1230,28 +1338,32 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int const frag_size =
|
unsigned int const ofrag_size =
|
||||||
info.attribute_value("frag_size", 0U);
|
info.attribute_value("ofrag_size", 0U);
|
||||||
unsigned int const frag_avail =
|
unsigned int const ofrag_avail =
|
||||||
info.attribute_value("frag_avail", 0U);
|
info.attribute_value("ofrag_avail", 0U);
|
||||||
if (!frag_avail || !frag_size) {
|
unsigned int const ofrag_total =
|
||||||
|
info.attribute_value("ofrag_total", 0U);
|
||||||
|
if (!ofrag_size || !ofrag_total) {
|
||||||
result = ENOTSUP;
|
result = ENOTSUP;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int const fragsize = (int)frag_size;
|
int const fragments = (int)ofrag_avail;
|
||||||
int const fragments = (int)frag_avail;
|
int const fragstotal = (int)ofrag_total;
|
||||||
if (fragments < 0 || fragsize < 0) {
|
int const fragsize = (int)ofrag_size;
|
||||||
|
if (fragments < 0 || fragstotal < 0 || fragsize < 0) {
|
||||||
result = EINVAL;
|
result = EINVAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct audio_buf_info *buf_info =
|
struct audio_buf_info *buf_info =
|
||||||
(struct audio_buf_info*)argp;
|
(struct audio_buf_info *)argp;
|
||||||
|
|
||||||
buf_info->fragments = fragments;
|
buf_info->fragments = fragments;
|
||||||
buf_info->fragsize = fragsize;
|
buf_info->fragstotal = fragstotal;
|
||||||
buf_info->bytes = fragments * fragsize;
|
buf_info->fragsize = fragsize;
|
||||||
|
buf_info->bytes = fragments * fragsize;
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
});
|
});
|
||||||
@ -1259,6 +1371,32 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return Fn::COMPLETE;
|
return Fn::COMPLETE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_GETPLAYVOL) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
int *vol = (int *)argp;
|
||||||
|
|
||||||
|
*vol = 100;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_LOW_WATER) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
int *val = (int *)argp;
|
||||||
|
|
||||||
|
*val = 0;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_NONBLOCK) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
} else if (request == SNDCTL_DSP_POST) {
|
} else if (request == SNDCTL_DSP_POST) {
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
@ -1282,7 +1420,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int const requested_fmt = *(int const*)argp;
|
int const requested_fmt = *(int const *)argp;
|
||||||
|
|
||||||
if (requested_fmt != (int)format) {
|
if (requested_fmt != (int)format) {
|
||||||
result = ENOTSUP;
|
result = ENOTSUP;
|
||||||
@ -1297,33 +1435,77 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
|
|
||||||
} else if (request == SNDCTL_DSP_SETFRAGMENT) {
|
} else if (request == SNDCTL_DSP_SETFRAGMENT) {
|
||||||
|
|
||||||
|
int *frag = (int *)argp;
|
||||||
|
int max_fragments = *frag >> 16;
|
||||||
|
int size_selector = *frag & ((1<<16) - 1);
|
||||||
|
|
||||||
|
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([&] {
|
monitor().monitor([&] {
|
||||||
|
|
||||||
_with_info(*fd, [&] (Xml_node info) {
|
_with_info(*fd, [&] (Xml_node info) {
|
||||||
if (info.type() != "oss") {
|
if (info.type() != "oss") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int const frag_size =
|
unsigned int const ofrag_size =
|
||||||
info.attribute_value("frag_size", 0U);
|
info.attribute_value("ofrag_size", 0U);
|
||||||
unsigned int const frag_size_log2 =
|
unsigned int const ofrag_size_log2 =
|
||||||
frag_size ? Genode::log2(frag_size) : 0;
|
ofrag_size ? Genode::log2(ofrag_size) : 0;
|
||||||
|
|
||||||
unsigned int const queue_size =
|
unsigned int const ofrag_total =
|
||||||
info.attribute_value("queue_size", 0U);
|
info.attribute_value("ofrag_total", 0U);
|
||||||
|
|
||||||
if (!queue_size || !frag_size_log2) {
|
if (!ofrag_total || !ofrag_size_log2) {
|
||||||
result = ENOTSUP;
|
result = ENOTSUP;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ignore the given hint */
|
|
||||||
|
|
||||||
handled = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Fn::COMPLETE;
|
return Fn::COMPLETE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_SETPLAYVOL) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
int *vol = (int *)argp;
|
||||||
|
|
||||||
|
*vol = 100;
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_DSP_SETTRIGGER) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
handled = true;
|
||||||
|
|
||||||
} else if (request == SNDCTL_DSP_SPEED) {
|
} else if (request == SNDCTL_DSP_SPEED) {
|
||||||
|
|
||||||
monitor().monitor([&] {
|
monitor().monitor([&] {
|
||||||
@ -1339,7 +1521,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int const speed = *(int const*)argp;
|
int const speed = *(int const *)argp;
|
||||||
if (speed < 0) {
|
if (speed < 0) {
|
||||||
result = EINVAL;
|
result = EINVAL;
|
||||||
return;
|
return;
|
||||||
@ -1350,7 +1532,7 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(int*)argp = samplerate;
|
*(int *)argp = samplerate;
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
});
|
});
|
||||||
@ -1358,6 +1540,14 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
|||||||
return Fn::COMPLETE;
|
return Fn::COMPLETE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} else if (request == SNDCTL_SYSINFO) {
|
||||||
|
|
||||||
|
/* dummy implementation */
|
||||||
|
|
||||||
|
oss_sysinfo *si = (oss_sysinfo *)argp;
|
||||||
|
Genode::memset(si, 0, sizeof(*si));
|
||||||
|
|
||||||
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1383,13 +1573,24 @@ int Libc::Vfs_plugin::ioctl(File_descriptor *fd, unsigned long request, char *ar
|
|||||||
case DIOCGMEDIASIZE:
|
case DIOCGMEDIASIZE:
|
||||||
result = _ioctl_dio(fd, request, argp);
|
result = _ioctl_dio(fd, request, argp);
|
||||||
break;
|
break;
|
||||||
|
case OSS_GETVERSION:
|
||||||
case SNDCTL_DSP_CHANNELS:
|
case SNDCTL_DSP_CHANNELS:
|
||||||
|
case SNDCTL_DSP_CURRENT_OPTR:
|
||||||
|
case SNDCTL_DSP_GETERROR:
|
||||||
|
case SNDCTL_DSP_GETISPACE:
|
||||||
|
case SNDCTL_DSP_GETOPTR:
|
||||||
case SNDCTL_DSP_GETOSPACE:
|
case SNDCTL_DSP_GETOSPACE:
|
||||||
|
case SNDCTL_DSP_GETPLAYVOL:
|
||||||
|
case SNDCTL_DSP_LOW_WATER:
|
||||||
|
case SNDCTL_DSP_NONBLOCK:
|
||||||
case SNDCTL_DSP_POST:
|
case SNDCTL_DSP_POST:
|
||||||
case SNDCTL_DSP_RESET:
|
case SNDCTL_DSP_RESET:
|
||||||
case SNDCTL_DSP_SAMPLESIZE:
|
case SNDCTL_DSP_SAMPLESIZE:
|
||||||
case SNDCTL_DSP_SETFRAGMENT:
|
case SNDCTL_DSP_SETFRAGMENT:
|
||||||
|
case SNDCTL_DSP_SETPLAYVOL:
|
||||||
|
case SNDCTL_DSP_SETTRIGGER:
|
||||||
case SNDCTL_DSP_SPEED:
|
case SNDCTL_DSP_SPEED:
|
||||||
|
case SNDCTL_SYSINFO:
|
||||||
result = _ioctl_sndctl(fd, request, argp);
|
result = _ioctl_sndctl(fd, request, argp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1,39 +1,56 @@
|
|||||||
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 session by providing a
|
||||||
file-system that can be mounted at arbitrary location within the VFS of a
|
file-system that can be mounted at arbitrary location within the VFS of a
|
||||||
component. It exposes a data file that can by used as 'dsp' file, e.g., _/dev/dsp_
|
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
|
as is common with OSS. The support I/O control operations or rather the
|
||||||
properties of the pseudo-device are provide 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" sample_rate="44000" format="16"
|
||||||
! queue_size="256" frag_size="1024" frag_avail="256"/>
|
! ofrag_total="256" ofrag_size="2048" ofrag_avail="256"
|
||||||
|
| 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:
|
||||||
|
|
||||||
* :channels: number of available channels. Set to 2 (stereo).
|
* :channels (ro): number of available channels. Set to 2 (stereo).
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_CHANNELS'
|
||||||
|
|
||||||
* :sample_rate: 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'
|
||||||
|
|
||||||
* :format: sample format, e.g. s16le. Defaults to AFMT_S16_LE.
|
* :format (ro): sample format, e.g. s16le. Defaults to AFMT_S16_LE.
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_SAMPLESIZE'
|
||||||
|
|
||||||
* :queue_size: number of packets in the underlying Audio_out session's
|
* :ofrag_total (rw): total number of output fragments. Set to number of packets
|
||||||
packet-stream.
|
in the underlying Audio_out session's packet-stream by default.
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_SETFRAGMENT', 'SNDCTL_DSP_GETOSPACE'
|
||||||
|
|
||||||
* :frag_size: size of a fragment. Set to 1024 (number of channels times
|
* :ofrag_size (rw): size of an output fragment. Set to 2048 (number of channels
|
||||||
size of Audio_out period times size of s16le sample).
|
times size of Audio_out period times size of s16le sample) by default.
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_SETFRAGMENT', 'SNDCTL_DSP_GETOSPACE'
|
||||||
|
|
||||||
* :frag_avail: number of available bytes. Initially set to queue size
|
* :ofrag_avail (ro): number of available output fragments. Initially set to total
|
||||||
times frag size.
|
fragment count.
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_GETOSPACE'
|
||||||
|
|
||||||
In its current state it is merely enough to use simply applications requiring
|
* :optr_samples (ro): total number of samples submitted to the Audio_out session
|
||||||
nothing more than a minimal set of the OSSv3 API. It does not allow altering
|
Corresponding OSS commands: 'SNDCTL_DSP_CURRENT_OPTR'
|
||||||
of parameters. Therefore it will only work when 44000Hz/s16le is used.
|
|
||||||
|
|
||||||
The following config snippets illustrates its configuration:
|
* :optr_fifo_samples (ro): number of samples residing in the Audio_out stream
|
||||||
|
Corresponding OSS commands: 'SNDCTL_DSP_CURRENT_OPTR'
|
||||||
|
|
||||||
|
* :play_underruns (rw): number of detected underrun errors, Writing anything
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
The following config snippet illustrates its configuration:
|
||||||
|
|
||||||
! <vfs>
|
! <vfs>
|
||||||
! <dir name="dev">
|
! <dir name="dev">
|
||||||
|
@ -15,16 +15,20 @@
|
|||||||
#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>
|
||||||
#include <gems/magic_ring_buffer.h>
|
#include <os/vfs.h>
|
||||||
#include <util/xml_generator.h>
|
#include <util/xml_generator.h>
|
||||||
#include <vfs/dir_file_system.h>
|
#include <vfs/dir_file_system.h>
|
||||||
#include <vfs/readonly_value_file_system.h>
|
#include <vfs/readonly_value_file_system.h>
|
||||||
#include <vfs/single_file_system.h>
|
#include <vfs/single_file_system.h>
|
||||||
|
#include <vfs/value_file_system.h>
|
||||||
|
|
||||||
/* libc includes */
|
/* libc includes */
|
||||||
#include <sys/soundcard.h>
|
#include <sys/soundcard.h>
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr size_t _stream_packet_size { Audio_out::PERIOD * Audio_out::SAMPLE_SIZE };
|
||||||
|
|
||||||
|
|
||||||
namespace Vfs { struct Oss_file_system; }
|
namespace Vfs { struct Oss_file_system; }
|
||||||
|
|
||||||
|
|
||||||
@ -46,51 +50,67 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
|
|
||||||
struct Info
|
struct Info
|
||||||
{
|
{
|
||||||
using Ro_fs = Readonly_value_file_system<unsigned>;
|
unsigned channels;
|
||||||
|
unsigned format;
|
||||||
|
unsigned sample_rate;
|
||||||
|
unsigned ofrag_total;
|
||||||
|
unsigned ofrag_size;
|
||||||
|
unsigned ofrag_avail;
|
||||||
|
long long optr_samples;
|
||||||
|
unsigned optr_fifo_samples;
|
||||||
|
unsigned play_underruns;
|
||||||
|
|
||||||
unsigned channels;
|
Readonly_value_file_system<unsigned> &_channels_fs;
|
||||||
unsigned format;
|
Readonly_value_file_system<unsigned> &_format_fs;
|
||||||
unsigned sample_rate;
|
Readonly_value_file_system<unsigned> &_sample_rate_fs;
|
||||||
unsigned queue_size;
|
Value_file_system<unsigned> &_ofrag_total_fs;
|
||||||
unsigned ofrag_size;
|
Value_file_system<unsigned> &_ofrag_size_fs;
|
||||||
unsigned ofrag_avail;
|
Readonly_value_file_system<unsigned> &_ofrag_avail_fs;
|
||||||
|
Readonly_value_file_system<long long> &_optr_samples_fs;
|
||||||
|
Readonly_value_file_system<unsigned> &_optr_fifo_samples_fs;
|
||||||
|
Value_file_system<unsigned> &_play_underruns_fs;
|
||||||
|
|
||||||
Ro_fs &_channels_fs;
|
Info(Readonly_value_file_system<unsigned> &channels_fs,
|
||||||
Ro_fs &_format_fs;
|
Readonly_value_file_system<unsigned> &format_fs,
|
||||||
Ro_fs &_sample_rate_fs;
|
Readonly_value_file_system<unsigned> &sample_rate_fs,
|
||||||
Ro_fs &_queue_size_fs;
|
Value_file_system<unsigned> &ofrag_total_fs,
|
||||||
Ro_fs &_ofrag_size_fs;
|
Value_file_system<unsigned> &ofrag_size_fs,
|
||||||
Ro_fs &_ofrag_avail_fs;
|
Readonly_value_file_system<unsigned> &ofrag_avail_fs,
|
||||||
|
Readonly_value_file_system<long long> &optr_samples_fs,
|
||||||
Info(Ro_fs &channels_fs,
|
Readonly_value_file_system<unsigned> &optr_fifo_samples_fs,
|
||||||
Ro_fs &format_fs,
|
Value_file_system<unsigned> &play_underruns_fs)
|
||||||
Ro_fs &queue_size_fs,
|
|
||||||
Ro_fs &sample_rate_fs,
|
|
||||||
Ro_fs &ofrag_size_fs,
|
|
||||||
Ro_fs &ofrag_avail_fs)
|
|
||||||
:
|
:
|
||||||
channels { 0 },
|
channels { 0 },
|
||||||
format { 0 },
|
format { 0 },
|
||||||
sample_rate { 0 },
|
sample_rate { 0 },
|
||||||
queue_size { 0 },
|
ofrag_total { 0 },
|
||||||
ofrag_size { 0 },
|
ofrag_size { 0 },
|
||||||
ofrag_avail { 0 },
|
ofrag_avail { 0 },
|
||||||
_channels_fs { channels_fs },
|
optr_samples { 0 },
|
||||||
_format_fs { format_fs },
|
optr_fifo_samples { 0 },
|
||||||
_sample_rate_fs { sample_rate_fs },
|
play_underruns { 0 },
|
||||||
_queue_size_fs { queue_size_fs },
|
_channels_fs { channels_fs },
|
||||||
_ofrag_size_fs { ofrag_size_fs },
|
_format_fs { format_fs },
|
||||||
_ofrag_avail_fs { ofrag_avail_fs }
|
_sample_rate_fs { sample_rate_fs },
|
||||||
|
_ofrag_total_fs { ofrag_total_fs },
|
||||||
|
_ofrag_size_fs { ofrag_size_fs },
|
||||||
|
_ofrag_avail_fs { ofrag_avail_fs },
|
||||||
|
_optr_samples_fs { optr_samples_fs },
|
||||||
|
_optr_fifo_samples_fs { optr_fifo_samples_fs },
|
||||||
|
_play_underruns_fs { play_underruns_fs }
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void update()
|
void update()
|
||||||
{
|
{
|
||||||
_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);
|
||||||
_queue_size_fs .value(queue_size);
|
_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);
|
||||||
|
_optr_samples_fs .value(optr_samples);
|
||||||
|
_optr_fifo_samples_fs.value(optr_fifo_samples);
|
||||||
|
_play_underruns_fs .value(play_underruns);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(Genode::Output &out) const
|
void print(Genode::Output &out) const
|
||||||
@ -98,12 +118,15 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
char buf[256] { };
|
char buf[256] { };
|
||||||
|
|
||||||
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("queue_size", queue_size);
|
xml.attribute("ofrag_total", ofrag_total);
|
||||||
xml.attribute("frag_size", ofrag_size);
|
xml.attribute("ofrag_size", ofrag_size);
|
||||||
xml.attribute("frag_avail", ofrag_avail);
|
xml.attribute("ofrag_avail", ofrag_avail);
|
||||||
|
xml.attribute("optr_samples", optr_samples);
|
||||||
|
xml.attribute("optr_fifo_samples", optr_fifo_samples);
|
||||||
|
xml.attribute("play_underruns", play_underruns);
|
||||||
});
|
});
|
||||||
|
|
||||||
Genode::print(out, Genode::Cstring(buf));
|
Genode::print(out, Genode::Cstring(buf));
|
||||||
@ -115,17 +138,7 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
Audio(Audio const &);
|
Audio(Audio const &);
|
||||||
Audio &operator = (Audio const &);
|
Audio &operator = (Audio const &);
|
||||||
|
|
||||||
/*
|
|
||||||
* Staging buffer
|
|
||||||
*
|
|
||||||
* Will later be used for resampling and re-coding, for
|
|
||||||
* the moment it de-couples the handle from the packet-stream.
|
|
||||||
*/
|
|
||||||
Genode::Magic_ring_buffer<float> _left_buffer;
|
|
||||||
Genode::Magic_ring_buffer<float> _right_buffer;
|
|
||||||
|
|
||||||
bool _started { false };
|
bool _started { false };
|
||||||
bool _buffer_full { 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" };
|
||||||
@ -133,16 +146,14 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
Genode::Constructible<Audio_out::Connection> _out[CHANNELS];
|
Genode::Constructible<Audio_out::Connection> _out[CHANNELS];
|
||||||
|
|
||||||
Info &_info;
|
Info &_info;
|
||||||
Readonly_value_file_system<Info> &_info_fs;
|
Readonly_value_file_system<Info, 256> &_info_fs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Audio(Genode::Env &env,
|
Audio(Genode::Env &env,
|
||||||
Info &info,
|
Info &info,
|
||||||
Readonly_value_file_system<Info> &info_fs)
|
Readonly_value_file_system<Info, 256> &info_fs)
|
||||||
:
|
:
|
||||||
_left_buffer { env, 1u << 20 },
|
|
||||||
_right_buffer { env, 1u << 20 },
|
|
||||||
_info { info },
|
_info { info },
|
||||||
_info_fs { info_fs }
|
_info_fs { info_fs }
|
||||||
{
|
{
|
||||||
@ -158,11 +169,11 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
_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.queue_size = Audio_out::QUEUE_SIZE;
|
_info.ofrag_total = Audio_out::QUEUE_SIZE;
|
||||||
_info.ofrag_size =
|
_info.ofrag_size =
|
||||||
(unsigned)Audio_out::PERIOD * (unsigned)CHANNELS
|
(unsigned)Audio_out::PERIOD * (unsigned)CHANNELS
|
||||||
* sizeof (short);
|
* sizeof (int16_t);
|
||||||
_info.ofrag_avail = _info.queue_size;
|
_info.ofrag_avail = _info.ofrag_total;
|
||||||
_info.update();
|
_info.update();
|
||||||
_info_fs.value(_info);
|
_info_fs.value(_info);
|
||||||
}
|
}
|
||||||
@ -191,14 +202,52 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
return _out[0]->stream()->queued();
|
return _out[0]->stream()->queued();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _queue_threshold_reached() const
|
void update_info_ofrag_avail_from_optr_fifo_samples()
|
||||||
{
|
{
|
||||||
return _out[0]->stream()->queued() > 20;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool need_data() const
|
/*
|
||||||
|
* Handle progress signal.
|
||||||
|
*
|
||||||
|
* Returns true if at least one stream packet is available.
|
||||||
|
*/
|
||||||
|
bool handle_progress()
|
||||||
{
|
{
|
||||||
return !_queue_threshold_reached();
|
unsigned fifo_samples_new = queued() * Audio_out::PERIOD;
|
||||||
|
|
||||||
|
if (fifo_samples_new == _info.optr_fifo_samples) {
|
||||||
|
/*
|
||||||
|
* This is usually the progress signal for the first
|
||||||
|
* packet after 'start()', which is invalid.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
if (fifo_samples_new > _info.optr_fifo_samples) {
|
||||||
|
_info.play_underruns++;
|
||||||
|
fifo_samples_new = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_info.optr_fifo_samples = fifo_samples_new;
|
||||||
|
update_info_ofrag_avail_from_optr_fifo_samples();
|
||||||
|
_info.update();
|
||||||
|
_info_fs.value(_info);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write(char const *buf, file_size buf_size, file_size &out_size)
|
bool write(char const *buf, file_size buf_size, file_size &out_size)
|
||||||
@ -207,40 +256,57 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
|
|
||||||
bool block_write = false;
|
bool block_write = false;
|
||||||
|
|
||||||
if (_queue_threshold_reached()) {
|
/*
|
||||||
|
* 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;
|
||||||
block_write = true;
|
block_write = true;
|
||||||
} else {
|
|
||||||
|
|
||||||
out_size = 0;
|
|
||||||
|
|
||||||
size_t const samples =
|
|
||||||
min(_left_buffer.write_avail(), buf_size/2);
|
|
||||||
|
|
||||||
float *dest[2] = { _left_buffer.write_addr(), _right_buffer.write_addr() };
|
|
||||||
|
|
||||||
for (size_t i = 0; i < samples/2; i++) {
|
|
||||||
|
|
||||||
for (int c = 0; c < CHANNELS; c++) {
|
|
||||||
float *p = dest[c];
|
|
||||||
int16_t const v = ((int16_t const*)buf)[i * CHANNELS + c];
|
|
||||||
p[i] = ((float)v) / 32768.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_left_buffer.fill(samples/2);
|
|
||||||
_right_buffer.fill(samples/2);
|
|
||||||
|
|
||||||
out_size += (samples * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (_left_buffer.read_avail() >= Audio_out::PERIOD) {
|
if (stream_packets_to_write == 0) {
|
||||||
|
out_size = 0;
|
||||||
|
throw Vfs::File_io_service::Insufficient_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
if (!_started) {
|
if (!_started) {
|
||||||
_started = true;
|
_started = true;
|
||||||
|
_out[0]->start();
|
||||||
|
_out[1]->start();
|
||||||
|
}
|
||||||
|
|
||||||
_out[0]->start();
|
for (unsigned packet_count = 0;
|
||||||
_out[1]->start();
|
packet_count < stream_packets_to_write;
|
||||||
}
|
packet_count++) {
|
||||||
|
|
||||||
Audio_out::Packet *lp = nullptr;
|
Audio_out::Packet *lp = nullptr;
|
||||||
|
|
||||||
@ -257,21 +323,48 @@ struct Vfs::Oss_file_system::Audio
|
|||||||
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 const *src[CHANNELS] = { _left_buffer.read_addr(),
|
float *dest[CHANNELS] = { lp->content(), rp->content() };
|
||||||
_right_buffer.read_addr() };
|
|
||||||
|
|
||||||
lp->content(src[0], Audio_out::PERIOD);
|
for (unsigned sample_count = 0;
|
||||||
_left_buffer.drain(Audio_out::PERIOD);
|
sample_count < Audio_out::PERIOD;
|
||||||
rp->content(src[1], Audio_out::PERIOD);
|
sample_count++) {
|
||||||
_right_buffer.drain(Audio_out::PERIOD);
|
|
||||||
|
for (unsigned c = 0; c < CHANNELS; c++) {
|
||||||
|
|
||||||
|
unsigned const buf_index =
|
||||||
|
(packet_count * Audio_out::PERIOD * CHANNELS) +
|
||||||
|
(sample_count * CHANNELS) + c;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_out[0]->submit(lp);
|
_out[0]->submit(lp);
|
||||||
_out[1]->submit(rp);
|
_out[1]->submit(rp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update */
|
out_size = Genode::min(stream_packets_to_write *
|
||||||
unsigned const new_avail = Audio_out::QUEUE_SIZE - _out[0]->stream()->queued();
|
_stream_packet_size, buf_size);
|
||||||
_info.ofrag_avail = new_avail;
|
|
||||||
|
/* 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.update();
|
||||||
_info_fs.value(_info);
|
_info_fs.value(_info);
|
||||||
|
|
||||||
@ -308,10 +401,16 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
|
|||||||
_audio { audio }
|
_audio { audio }
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
Read_result read(char *, file_size, file_size &) override
|
Read_result read(char *buf, file_size buf_size, file_size &out_count) override
|
||||||
{
|
{
|
||||||
/* not supported */
|
/* dummy implementation with audible noise for testing */
|
||||||
return READ_ERR_INVALID;
|
|
||||||
|
for (file_size i = 0; i < buf_size; i++)
|
||||||
|
buf[i] = i;
|
||||||
|
|
||||||
|
out_count = buf_size;
|
||||||
|
|
||||||
|
return READ_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Write_result write(char const *buf, file_size buf_size,
|
Write_result write(char const *buf, file_size buf_size,
|
||||||
@ -346,17 +445,15 @@ class Vfs::Oss_file_system::Data_file_system : public Single_file_system
|
|||||||
|
|
||||||
void _handle_progress()
|
void _handle_progress()
|
||||||
{
|
{
|
||||||
unsigned const queued = _audio.queued();
|
if (_audio.handle_progress()) {
|
||||||
if (!queued) {
|
/* at least one stream packet is available */
|
||||||
_audio.pause();
|
_handle_registry.for_each([this] (Registered_handle &handle) {
|
||||||
|
if (handle.blocked) {
|
||||||
|
handle.blocked = false;
|
||||||
|
handle.io_progress_response();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_handle_registry.for_each([this] (Registered_handle &handle) {
|
|
||||||
if (handle.blocked) {
|
|
||||||
handle.blocked = false;
|
|
||||||
handle.io_progress_response();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -424,20 +521,98 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
|
|||||||
|
|
||||||
Vfs::Env &_env;
|
Vfs::Env &_env;
|
||||||
|
|
||||||
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 };
|
||||||
Readonly_value_file_system<unsigned> _queue_size_fs { "queue_size", 0U };
|
Value_file_system<unsigned> _ofrag_total_fs { "ofrag_total", 0U };
|
||||||
Readonly_value_file_system<unsigned> _ofrag_size_fs { "frag_size", 0U} ;
|
Value_file_system<unsigned> _ofrag_size_fs { "ofrag_size", 0U} ;
|
||||||
Readonly_value_file_system<unsigned> _ofrag_avail_fs { "frag_avail", 0U };
|
Readonly_value_file_system<unsigned> _ofrag_avail_fs { "ofrag_avail", 0U };
|
||||||
|
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 };
|
||||||
|
Value_file_system<unsigned> _play_underruns_fs { "play_underruns", 0U };
|
||||||
|
|
||||||
Audio::Info _info { _channels_fs, _format_fs, _sample_rate_fs,
|
Audio::Info _info { _channels_fs, _format_fs, _sample_rate_fs,
|
||||||
_queue_size_fs, _ofrag_size_fs, _ofrag_avail_fs };
|
_ofrag_total_fs, _ofrag_size_fs, _ofrag_avail_fs,
|
||||||
|
_optr_samples_fs, _optr_fifo_samples_fs,
|
||||||
|
_play_underruns_fs };
|
||||||
|
|
||||||
Readonly_value_file_system<Audio::Info> _info_fs { "info", _info };
|
Readonly_value_file_system<Audio::Info, 256> _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> _ofrag_total_handler {
|
||||||
|
_ofrag_total_fs, "/ofrag_total",
|
||||||
|
_env.alloc(),
|
||||||
|
*this,
|
||||||
|
&Vfs::Oss_file_system::Local_factory::_ofrag_total_changed };
|
||||||
|
|
||||||
|
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _ofrag_size_handler {
|
||||||
|
_ofrag_size_fs, "/ofrag_size",
|
||||||
|
_env.alloc(),
|
||||||
|
*this,
|
||||||
|
&Vfs::Oss_file_system::Local_factory::_ofrag_size_changed };
|
||||||
|
|
||||||
|
Genode::Watch_handler<Vfs::Oss_file_system::Local_factory> _play_underruns_handler {
|
||||||
|
_play_underruns_fs, "/play_underruns",
|
||||||
|
_env.alloc(),
|
||||||
|
*this,
|
||||||
|
&Vfs::Oss_file_system::Local_factory::_play_underruns_changed };
|
||||||
|
|
||||||
|
static constexpr size_t _native_stream_size { Audio_out::QUEUE_SIZE * _stream_packet_size };
|
||||||
|
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 };
|
||||||
|
|
||||||
|
/********************
|
||||||
|
** Watch handlers **
|
||||||
|
********************/
|
||||||
|
|
||||||
|
void _ofrag_total_changed()
|
||||||
|
{
|
||||||
|
unsigned ofrag_total_new = _ofrag_total_fs.value();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
_info.ofrag_total = ofrag_total_new;
|
||||||
|
_info.ofrag_avail = ofrag_total_new;
|
||||||
|
|
||||||
|
_info.update();
|
||||||
|
_info_fs.value(_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _ofrag_size_changed()
|
||||||
|
{
|
||||||
|
unsigned ofrag_size_new = _ofrag_size_fs.value();
|
||||||
|
|
||||||
|
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;
|
||||||
|
_info.ofrag_avail = _info.ofrag_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
_info.ofrag_size = ofrag_size_new;
|
||||||
|
|
||||||
|
_info.update();
|
||||||
|
_info_fs.value(_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _play_underruns_changed()
|
||||||
|
{
|
||||||
|
/* reset counter */
|
||||||
|
|
||||||
|
_info.play_underruns = 0;
|
||||||
|
|
||||||
|
_info.update();
|
||||||
|
_info_fs.value(_info);
|
||||||
|
}
|
||||||
|
|
||||||
static Name name(Xml_node config)
|
static Name name(Xml_node config)
|
||||||
{
|
{
|
||||||
return config.attribute_value("name", Name("oss"));
|
return config.attribute_value("name", Name("oss"));
|
||||||
@ -469,10 +644,6 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
|
|||||||
return &_channels_fs;
|
return &_channels_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queue_size_fs.matches(node)) {
|
|
||||||
return &_queue_size_fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sample_rate_fs.matches(node)) {
|
if (_sample_rate_fs.matches(node)) {
|
||||||
return &_sample_rate_fs;
|
return &_sample_rate_fs;
|
||||||
}
|
}
|
||||||
@ -481,12 +652,31 @@ struct Vfs::Oss_file_system::Local_factory : File_system_factory
|
|||||||
return &_ofrag_avail_fs;
|
return &_ofrag_avail_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_format_fs.matches(node)) {
|
||||||
|
return &_format_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_optr_samples_fs.matches(node)) {
|
||||||
|
return &_optr_samples_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_optr_fifo_samples_fs.matches(node)) {
|
||||||
|
return &_optr_fifo_samples_fs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has_type(Value_file_system<unsigned>::type_name())) {
|
||||||
|
|
||||||
|
if (_ofrag_total_fs.matches(node)) {
|
||||||
|
return &_ofrag_total_fs;
|
||||||
|
}
|
||||||
|
|
||||||
if (_ofrag_size_fs.matches(node)) {
|
if (_ofrag_size_fs.matches(node)) {
|
||||||
return &_ofrag_size_fs;
|
return &_ofrag_size_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_format_fs.matches(node)) {
|
if (_play_underruns_fs.matches(node)) {
|
||||||
return &_format_fs;
|
return &_play_underruns_fs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,16 +723,28 @@ class Vfs::Oss_file_system::Compound_file_system : private Local_factory,
|
|||||||
xml.attribute("name", "format");
|
xml.attribute("name", "format");
|
||||||
});
|
});
|
||||||
|
|
||||||
xml.node("readonly_value", [&] {
|
xml.node("value", [&] {
|
||||||
xml.attribute("name", "queue_size");
|
xml.attribute("name", "ofrag_total");
|
||||||
|
});
|
||||||
|
|
||||||
|
xml.node("value", [&] {
|
||||||
|
xml.attribute("name", "ofrag_size");
|
||||||
});
|
});
|
||||||
|
|
||||||
xml.node("readonly_value", [&] {
|
xml.node("readonly_value", [&] {
|
||||||
xml.attribute("name", "frag_size");
|
xml.attribute("name", "ofrag_avail");
|
||||||
});
|
});
|
||||||
|
|
||||||
xml.node("readonly_value", [&] {
|
xml.node("readonly_value", [&] {
|
||||||
xml.attribute("name", "frag_avail");
|
xml.attribute("name", "optr_samples");
|
||||||
|
});
|
||||||
|
|
||||||
|
xml.node("readonly_value", [&] {
|
||||||
|
xml.attribute("name", "optr_fifo_samples");
|
||||||
|
});
|
||||||
|
|
||||||
|
xml.node("value", [&] {
|
||||||
|
xml.attribute("name", "play_underruns");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -75,7 +75,7 @@ class Vfs::Value_file_system : public Vfs::Single_file_system
|
|||||||
return WRITE_ERR_INVALID;
|
return WRITE_ERR_INVALID;
|
||||||
|
|
||||||
Genode::size_t const len = min(BUF_SIZE- seek(), count);
|
Genode::size_t const len = min(BUF_SIZE- seek(), count);
|
||||||
_buffer = Buffer(Cstring(src, len));
|
_buffer = Buffer(Genode::Cstring(src, len));
|
||||||
out_count = len;
|
out_count = len;
|
||||||
|
|
||||||
/* inform watchers */
|
/* inform watchers */
|
Loading…
x
Reference in New Issue
Block a user