mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-14 22:42:54 +00:00
In addition to updating the contrib sources the driver now uses the new Component API and will report the internal mixer state. Reporting of the mixer state is enabled by adding the 'report_mixer' attribute to the drivers configuration and setting its value to 'yes'. The following snippets illustrates the format of the report: !<mixer_state> ! <mixer field="inputs.beep" value="108"/> ! <mixer field="outputs.hp_sense" value="plugged"/> ! <mixer field="outputs.master" value="128,128"/> ! <mixer field="outputs.mic_sense" value="unplugged"/> ! <mixer field="outputs.spkr_muters" value="hp,mic"/> !</mixer_state> The mixer state may expose other mixer fields as well, depending on the used sound card. The naming scheme of the attributes intentionally matches the naming scheme of OpenBSD's mixerctl(1) program. Each 'mixer' node can be used to configure the audio driver by using it in its configuration, e.g.: !<config report_mixer="yes"> ! <mixer field="outputs.master" value="255,255"/> !</config> This configuration will set the output volume to the highest possible value. Although it is now also possible to update the configuration at run-time it should not be done while the driver is currently playing or recording because it may provoke the generation of artefacts. Fixes #1973.
413 lines
9.0 KiB
C++
413 lines
9.0 KiB
C++
/**
|
|
* \brief Audio driver BSD API emulation
|
|
* \author Josef Soentgen
|
|
* \date 2014-11-16
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-2016 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <base/allocator_avl.h>
|
|
#include <base/env.h>
|
|
#include <base/log.h>
|
|
#include <dataspace/client.h>
|
|
#include <rm_session/connection.h>
|
|
#include <region_map/client.h>
|
|
#include <util/string.h>
|
|
|
|
/* local includes */
|
|
#include <bsd.h>
|
|
#include <bsd_emul.h>
|
|
|
|
|
|
static bool const verbose = false;
|
|
|
|
|
|
namespace Bsd {
|
|
typedef Genode::addr_t addr_t;
|
|
|
|
class Slab_backend_alloc;
|
|
class Slab_alloc;
|
|
class Malloc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Back-end allocator for Genode's slab allocator
|
|
*/
|
|
class Bsd::Slab_backend_alloc : public Genode::Allocator,
|
|
public Genode::Rm_connection,
|
|
public Genode::Region_map_client
|
|
{
|
|
private:
|
|
|
|
enum {
|
|
VM_SIZE = 8 * 1024 * 1024, /* size of VM region to reserve */
|
|
BLOCK_SIZE = 1024 * 1024, /* 1 MiB */
|
|
ELEMENTS = VM_SIZE / BLOCK_SIZE, /* MAX number of dataspaces in VM */
|
|
};
|
|
|
|
addr_t _base; /* virt. base address */
|
|
Genode::Ram_dataspace_capability _ds_cap[ELEMENTS]; /* dataspaces to put in VM */
|
|
addr_t _ds_phys[ELEMENTS]; /* physical bases of dataspaces */
|
|
int _index; /* current index in ds_cap */
|
|
Genode::Allocator_avl _range; /* manage allocations */
|
|
Genode::Ram_session &_ram; /* ram session to allocate ds from */
|
|
|
|
bool _alloc_block()
|
|
{
|
|
if (_index == ELEMENTS) {
|
|
Genode::error("Slab-backend exhausted!");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
_ds_cap[_index] = _ram.alloc(BLOCK_SIZE);
|
|
Region_map_client::attach_at(_ds_cap[_index], _index * BLOCK_SIZE, BLOCK_SIZE, 0);
|
|
} catch (...) { return false; }
|
|
|
|
/* return base + offset in VM area */
|
|
addr_t block_base = _base + (_index * BLOCK_SIZE);
|
|
++_index;
|
|
|
|
_range.add_range(block_base, BLOCK_SIZE);
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
|
|
Slab_backend_alloc(Genode::Ram_session &ram, Genode::Region_map &rm,
|
|
Genode::Allocator &md_alloc)
|
|
:
|
|
Region_map_client(Rm_connection::create(VM_SIZE)),
|
|
_index(0), _range(&md_alloc), _ram(ram)
|
|
{
|
|
/* reserver attach us, anywere */
|
|
_base = rm.attach(dataspace());
|
|
}
|
|
|
|
addr_t start() const { return _base; }
|
|
addr_t end() const { return _base + VM_SIZE - 1; }
|
|
|
|
|
|
/*************************
|
|
** Allocator interface **
|
|
*************************/
|
|
|
|
bool alloc(Genode::size_t size, void **out_addr)
|
|
{
|
|
bool done = _range.alloc(size, out_addr);
|
|
|
|
if (done)
|
|
return done;
|
|
|
|
done = _alloc_block();
|
|
if (!done) {
|
|
Genode::error("Backend allocator exhausted\n");
|
|
return false;
|
|
}
|
|
|
|
return _range.alloc(size, out_addr);
|
|
}
|
|
|
|
void free(void *addr, Genode::size_t size) { _range.free(addr, size); }
|
|
Genode::size_t overhead(Genode::size_t size) const { return 0; }
|
|
bool need_size_for_free() const override { return false; }
|
|
};
|
|
|
|
|
|
/**
|
|
* Slab allocator using our back-end allocator
|
|
*/
|
|
class Bsd::Slab_alloc : public Genode::Slab
|
|
{
|
|
private:
|
|
|
|
Genode::size_t const _object_size;
|
|
|
|
static Genode::size_t _calculate_block_size(Genode::size_t object_size)
|
|
{
|
|
Genode::size_t const block_size = 16*object_size;
|
|
return Genode::align_addr(block_size, 12);
|
|
}
|
|
|
|
public:
|
|
|
|
Slab_alloc(Genode::size_t object_size, Slab_backend_alloc &allocator)
|
|
:
|
|
Slab(object_size, _calculate_block_size(object_size), 0, &allocator),
|
|
_object_size(object_size)
|
|
{ }
|
|
|
|
Genode::addr_t alloc()
|
|
{
|
|
Genode::addr_t result;
|
|
return (Slab::alloc(_object_size, (void **)&result) ? result : 0);
|
|
}
|
|
|
|
void free(void *ptr) { Slab::free(ptr, _object_size); }
|
|
};
|
|
|
|
|
|
/**
|
|
* Memory interface
|
|
*/
|
|
class Bsd::Malloc
|
|
{
|
|
private:
|
|
|
|
enum {
|
|
SLAB_START_LOG2 = 5, /* 32 B */
|
|
SLAB_STOP_LOG2 = 16, /* 64 KiB */
|
|
NUM_SLABS = (SLAB_STOP_LOG2 - SLAB_START_LOG2) + 1,
|
|
};
|
|
|
|
typedef Genode::addr_t addr_t;
|
|
typedef Bsd::Slab_alloc Slab_alloc;
|
|
typedef Bsd::Slab_backend_alloc Slab_backend_alloc;
|
|
|
|
Slab_backend_alloc &_back_allocator;
|
|
Slab_alloc *_allocator[NUM_SLABS];
|
|
addr_t _start;
|
|
addr_t _end;
|
|
|
|
/**
|
|
* Set 'value' at 'addr'
|
|
*/
|
|
void _set_at(addr_t addr, addr_t value) { *((addr_t *)addr) = value; }
|
|
|
|
/**
|
|
* Retrieve slab index belonging to given address
|
|
*/
|
|
unsigned _slab_index(Genode::addr_t **addr)
|
|
{
|
|
using namespace Genode;
|
|
/* get index */
|
|
addr_t index = *(*addr - 1);
|
|
|
|
/*
|
|
* If index large, we use aligned memory, retrieve beginning of slab entry
|
|
* and read index from there
|
|
*/
|
|
if (index > 32) {
|
|
*addr = (addr_t *)*(*addr - 1);
|
|
index = *(*addr - 1);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Get the originally requested size of the allocation
|
|
*/
|
|
Genode::size_t _get_orig_size(Genode::addr_t **addr)
|
|
{
|
|
using namespace Genode;
|
|
|
|
addr_t index = *(*addr - 1);
|
|
if (index > 32) {
|
|
*addr = (addr_t *) * (*addr - 1);
|
|
}
|
|
|
|
return *(*addr - 2);
|
|
}
|
|
|
|
public:
|
|
|
|
Malloc(Slab_backend_alloc &alloc, Genode::Allocator &md_alloc)
|
|
:
|
|
_back_allocator(alloc), _start(alloc.start()),
|
|
_end(alloc.end())
|
|
{
|
|
/* init slab allocators */
|
|
for (unsigned i = SLAB_START_LOG2; i <= SLAB_STOP_LOG2; i++) {
|
|
_allocator[i - SLAB_START_LOG2] =
|
|
new (&md_alloc) Slab_alloc(1U << i, _back_allocator);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Alloc in slabs
|
|
*/
|
|
void *alloc(Genode::size_t size, int align = 0)
|
|
{
|
|
using namespace Genode;
|
|
|
|
/* save requested size */
|
|
Genode::size_t orig_size = size;
|
|
size += sizeof(addr_t);
|
|
|
|
/* += slab index + aligment size */
|
|
size += sizeof(addr_t) + (align > 2 ? (1 << align) : 0);
|
|
|
|
int msb = Genode::log2(size);
|
|
|
|
if (size > (1U << msb))
|
|
msb++;
|
|
|
|
if (size < (1U << SLAB_START_LOG2))
|
|
msb = SLAB_STOP_LOG2;
|
|
|
|
if (msb > SLAB_STOP_LOG2) {
|
|
Genode::error("Slab too large ", 1U << msb, " reqested ", size);
|
|
return 0;
|
|
}
|
|
|
|
addr_t addr = _allocator[msb - SLAB_START_LOG2]->alloc();
|
|
if (!addr) {
|
|
Genode::error("Failed to get slab for ", 1U << msb);
|
|
return 0;
|
|
}
|
|
|
|
_set_at(addr, orig_size);
|
|
addr += sizeof(addr_t);
|
|
|
|
_set_at(addr, msb - SLAB_START_LOG2);
|
|
addr += sizeof(addr_t);
|
|
|
|
if (align > 2) {
|
|
/* save */
|
|
addr_t ptr = addr;
|
|
addr_t align_val = (1U << align);
|
|
addr_t align_mask = align_val - 1;
|
|
/* align */
|
|
addr = (addr + align_val) & ~align_mask;
|
|
/* write start address before aligned address */
|
|
_set_at(addr - sizeof(addr_t), ptr);
|
|
}
|
|
|
|
return (addr_t *)addr;
|
|
}
|
|
|
|
void free(void const *a)
|
|
{
|
|
using namespace Genode;
|
|
addr_t *addr = (addr_t *)a;
|
|
|
|
unsigned nr = _slab_index(&addr);
|
|
/* we need to decrease addr by 2, orig_size and index come first */
|
|
_allocator[nr]->free((void *)(addr - 2));
|
|
}
|
|
|
|
Genode::size_t size(void const *a)
|
|
{
|
|
using namespace Genode;
|
|
addr_t *addr = (addr_t *)a;
|
|
|
|
return _get_orig_size(&addr);
|
|
}
|
|
|
|
bool inside(addr_t const addr) const { return (addr > _start) && (addr <= _end); }
|
|
};
|
|
|
|
|
|
static Bsd::Malloc *_malloc;
|
|
|
|
|
|
void Bsd::mem_init(Genode::Env &env, Genode::Allocator &alloc)
|
|
{
|
|
static Bsd::Slab_backend_alloc sb(env.ram(), env.rm(), alloc);
|
|
static Bsd::Malloc m(sb, alloc);
|
|
_malloc = &m;
|
|
}
|
|
|
|
|
|
static Bsd::Malloc& malloc_backend() { return *_malloc; }
|
|
|
|
|
|
/**********************
|
|
** Memory allocation *
|
|
**********************/
|
|
|
|
extern "C" void *malloc(size_t size, int type, int flags)
|
|
{
|
|
void *addr = malloc_backend().alloc(size);
|
|
|
|
if (flags & M_ZERO)
|
|
Genode::memset(addr, 0, size);
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
extern "C" void *mallocarray(size_t nmemb, size_t size, int type, int flags)
|
|
{
|
|
if (size != 0 && nmemb > (~0UL / size))
|
|
return 0;
|
|
|
|
return malloc(nmemb * size, type, flags);
|
|
}
|
|
|
|
|
|
extern "C" void free(void *addr, int type, size_t size)
|
|
{
|
|
if (!addr) return;
|
|
|
|
if (!malloc_backend().inside((Genode::addr_t)addr)) {
|
|
Genode::error("cannot free unknown memory at ", __builtin_return_address(0),
|
|
" called from ", addr);
|
|
return;
|
|
}
|
|
|
|
if (size) {
|
|
size_t ssize = malloc_backend().size(addr);
|
|
|
|
if (ssize != size) {
|
|
Genode::warning("size: ", size, "for ", addr,
|
|
" does not match stored size: ", ssize);
|
|
}
|
|
}
|
|
|
|
malloc_backend().free(addr);
|
|
}
|
|
|
|
|
|
/*****************
|
|
** sys/systm.h **
|
|
*****************/
|
|
|
|
extern "C" void bzero(void *b, size_t len)
|
|
{
|
|
if (b == nullptr) return;
|
|
|
|
Genode::memset(b, 0, len);
|
|
}
|
|
|
|
|
|
extern "C" void bcopy(const void *src, void *dst, size_t len)
|
|
{
|
|
/* XXX may overlap */
|
|
Genode::memcpy(dst, src, len);
|
|
}
|
|
|
|
|
|
extern "C" int uiomove(void *buf, int n, struct uio *uio)
|
|
{
|
|
void *dst = nullptr;
|
|
void *src = nullptr;
|
|
size_t len = uio->uio_resid < (size_t)n ? uio->uio_resid : (size_t)n;
|
|
|
|
switch (uio->uio_rw) {
|
|
case UIO_READ:
|
|
dst = buf;
|
|
src = ((char*)uio->buf) + uio->uio_offset;
|
|
break;
|
|
case UIO_WRITE:
|
|
dst = ((char*)uio->buf) + uio->uio_offset;
|
|
src = buf;
|
|
break;
|
|
}
|
|
|
|
Genode::memcpy(dst, src, len);
|
|
|
|
uio->uio_resid -= len;
|
|
uio->uio_offset += len;
|
|
|
|
return 0;
|
|
}
|