genode/repos/os/src/drivers/audio/spec/linux/main.cc

310 lines
7.1 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Audio_out-out driver for Linux
2011-12-22 16:19:25 +01:00
* \author Christian Helmuth
* \author Sebastian Sumpf
* \author Josef Soentgen
2011-12-22 16:19:25 +01:00
* \date 2010-05-11
*
* FIXME session and driver shutdown not implemented (audio_drv_stop)
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2011-12-22 16:19:25 +01:00
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/log.h>
2011-12-22 16:19:25 +01:00
#include <base/sleep.h>
#include <base/heap.h>
2011-12-22 16:19:25 +01:00
#include <root/component.h>
#include <audio_out_session/rpc_object.h>
2011-12-22 16:19:25 +01:00
#include <util/misc_math.h>
#include <timer_session/connection.h>
2011-12-22 16:19:25 +01:00
/* local includes */
#include <alsa.h>
2011-12-22 16:19:25 +01:00
using namespace Genode;
2011-12-22 16:19:25 +01:00
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS };
2011-12-22 16:19:25 +01:00
namespace Audio_out
{
class Session_component;
class Out;
class Root;
struct Root_policy;
struct Main;
2011-12-22 16:19:25 +01:00
static Session_component *channel_acquired[MAX_CHANNELS];
};
2011-12-22 16:19:25 +01:00
class Audio_out::Session_component : public Audio_out::Session_rpc_object
{
private:
2011-12-22 16:19:25 +01:00
Channel_number _channel;
2011-12-22 16:19:25 +01:00
public:
2011-12-22 16:19:25 +01:00
Session_component(Genode::Env &env, Channel_number channel, Signal_context_capability data_cap)
:
Session_rpc_object(env, data_cap),
_channel(channel)
{
Audio_out::channel_acquired[_channel] = this;
}
2011-12-22 16:19:25 +01:00
~Session_component()
{
Audio_out::channel_acquired[_channel] = 0;
}
};
2011-12-22 16:19:25 +01:00
static bool channel_number_from_string(const char *name,
Channel_number *out_number)
{
static struct Names {
const char *name;
Channel_number number;
} names[] = {
{ "left", LEFT }, { "front left", LEFT },
{ "right", RIGHT }, { "front right", RIGHT },
{ 0, INVALID }
};
2011-12-22 16:19:25 +01:00
for (Names *n = names; n->name; ++n)
if (!strcmp(name, n->name)) {
*out_number = n->number;
return true;
}
2011-12-22 16:19:25 +01:00
return false;
}
2011-12-22 16:19:25 +01:00
/*
* Root component, handling new session requests.
*/
class Audio_out::Out
{
private:
2011-12-22 16:19:25 +01:00
Genode::Env &_env;
Genode::Signal_handler<Audio_out::Out> _data_avail_dispatcher;
Genode::Signal_handler<Audio_out::Out> _timer_dispatcher;
Timer::Connection _timer { _env };
bool _active() {
return channel_acquired[LEFT] && channel_acquired[RIGHT] &&
channel_acquired[LEFT]->active() && channel_acquired[RIGHT]->active();
}
2011-12-22 16:19:25 +01:00
Stream *left() { return channel_acquired[LEFT]->stream(); }
Stream *right() { return channel_acquired[RIGHT]->stream(); }
void _advance_position(Packet *l, Packet *r)
2011-12-22 16:19:25 +01:00
{
bool full_left = left()->full();
bool full_right = right()->full();
2011-12-22 16:19:25 +01:00
left()->pos(left()->packet_position(l));
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
right()->pos(right()->packet_position(r));
left()->increment_position();
right()->increment_position();
Session_component *channel_left = channel_acquired[LEFT];
Session_component *channel_right = channel_acquired[RIGHT];
if (full_left)
channel_left->alloc_submit();
if (full_right)
channel_right->alloc_submit();
channel_left->progress_submit();
channel_right->progress_submit();
2011-12-22 16:19:25 +01:00
}
bool _play_packet()
{
Packet *p_left = left()->get(left()->pos());
Packet *p_right = right()->get(left()->pos());
/* convert float to S16LE */
static short data[2 * PERIOD];
2011-12-22 16:19:25 +01:00
if (p_left->valid() && p_right->valid()) {
2011-12-22 16:19:25 +01:00
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
for (unsigned i = 0; i < 2 * PERIOD; i += 2) {
data[i] = p_left->content()[i / 2] * 32767;
data[i + 1] = p_right->content()[i / 2] * 32767;
}
p_left->invalidate();
p_right->invalidate();
/* blocking-write packet to ALSA */
while (audio_drv_play(data, PERIOD)) {
/* try to restart the driver silently */
audio_drv_stop();
audio_drv_start();
}
2011-12-22 16:19:25 +01:00
p_left->mark_as_played();
p_right->mark_as_played();
}
2011-12-22 16:19:25 +01:00
_advance_position(p_left, p_right);
2011-12-22 16:19:25 +01:00
return true;
}
2011-12-22 16:19:25 +01:00
void _handle_data_avail() { }
2011-12-22 16:19:25 +01:00
void _handle_timer()
{
if (_active()) _play_packet();
}
2011-12-22 16:19:25 +01:00
public:
Out(Genode::Env &env)
:
_env(env),
_data_avail_dispatcher(env.ep(), *this, &Audio_out::Out::_handle_data_avail),
_timer_dispatcher(env.ep(), *this, &Audio_out::Out::_handle_timer)
{
_timer.sigh(_timer_dispatcher);
2011-12-22 16:19:25 +01:00
uint64_t const us = (Audio_out::PERIOD * 1000 / Audio_out::SAMPLE_RATE)*1000;
_timer.trigger_periodic(us);
}
Signal_context_capability data_avail_sigh() { return _data_avail_dispatcher; }
};
/**
* Session creation policy for our service
*/
struct Audio_out::Root_policy
{
void aquire(const char *args)
{
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t session_size =
align_addr(sizeof(Audio_out::Session_component), 12);
if ((ram_quota < session_size) ||
(sizeof(Stream) > ram_quota - session_size)) {
Genode::error("insufficient 'ram_quota', got ", ram_quota,
" need ", sizeof(Stream) + session_size);
throw Genode::Insufficient_ram_quota();
}
char channel_name[16];
Channel_number channel_number;
Arg_string::find_arg(args, "channel").string(channel_name,
sizeof(channel_name),
"left");
if (!channel_number_from_string(channel_name, &channel_number))
throw Genode::Service_denied();
if (Audio_out::channel_acquired[channel_number])
throw Genode::Service_denied();
}
void release() { }
};
namespace Audio_out {
typedef Root_component<Session_component, Root_policy> Root_component;
2011-12-22 16:19:25 +01:00
}
class Audio_out::Root : public Audio_out::Root_component
{
private:
Genode::Env &_env;
Signal_context_capability _data_cap;
protected:
Session_component *_create_session(const char *args) override
{
char channel_name[16];
Channel_number channel_number = INVALID;
Arg_string::find_arg(args, "channel").string(channel_name,
sizeof(channel_name),
"left");
channel_number_from_string(channel_name, &channel_number);
return new (md_alloc())
Session_component(_env, channel_number, _data_cap);
}
public:
Root(Genode::Env &env, Allocator &md_alloc,
Signal_context_capability data_cap)
: Root_component(env.ep(), md_alloc), _env(env), _data_cap(data_cap)
{ }
};
struct Audio_out::Main
2011-12-22 16:19:25 +01:00
{
Genode::Env &env;
Genode::Heap heap { env.ram(), env.rm() };
Genode::Attached_rom_dataspace config { env, "config" };
2011-12-22 16:19:25 +01:00
Main(Genode::Env &env) : env(env)
{
char dev[32] = { 'h', 'w', 0 };
try {
config.xml().attribute("alsa_device").value(dev, sizeof(dev));
} catch (...) { }
/* init ALSA */
int err = audio_drv_init(dev);
if (err) {
if (err == -1) {
Genode::error("could not open ALSA device ", Genode::Cstring(dev));
} else {
Genode::error("could not initialize driver error ", err);
}
throw -1;
}
audio_drv_start();
static Audio_out::Out out(env);
static Audio_out::Root root(env, heap, out.data_avail_sigh());
env.parent().announce(env.ep().manage(root));
Genode::log("--- start Audio_out ALSA driver ---");
}
};
/***************
** Component **
***************/
2011-12-22 16:19:25 +01:00
void Component::construct(Genode::Env &env) { static Audio_out::Main main(env); }