dde_bsd: update audio driver to OpenBSD 6.6

Besides updating to a newer version the update adds the AC97 ICH driver
and addresses shortcomings with the OpenBSD emulation environment:

* Fix 'delay(9)' implementation - it now properly uses 'us' as unit,
  which results on faster initialization of the drivers.

* Fix LOG output that got lost during commit f23579532 and bring over
  the printf implementation from dde_linux for more structured
  printing.

* As said the driver now recognizes the AUICH devices. So far it was
  only tested with the device model in VirtualBox where it produces
  stuttering audio, investigating the cause is still ongoing.

Fixes #3641.
This commit is contained in:
Josef Söntgen
2019-10-16 11:57:44 +02:00
committed by Christian Helmuth
parent 9bd3d2aa5c
commit 9d7a58f6a7
16 changed files with 569 additions and 105 deletions

View File

@ -31,6 +31,8 @@ extern struct cfdriver azalia_cd;
extern struct cfattach azalia_ca;
extern struct cfdriver eap_cd;
extern struct cfattach eap_ca;
extern struct cfdriver auich_cd;
extern struct cfattach auich_ca;
/* original value */
@ -42,6 +44,7 @@ struct cfdata cfdata[] = {
{&audio_ca, &audio_cd, 0, 0, 0, 0, pv+0, 0, 0},
{&azalia_ca, &azalia_cd, 0, 0, 0, 0, pv+1, 0, 0},
{&eap_ca, &eap_cd, 0, 0, 0, 0, pv+1, 0, 0},
{&auich_ca, &auich_cd, 0, 0, 0, 0, pv+1, 0, 0},
};
@ -100,10 +103,13 @@ int probe_cfdata(struct pci_attach_args *pa)
struct device *dev = (struct device *) malloc(ca->ca_devsize,
M_DEVBUF, M_NOWAIT|M_ZERO);
dev->dv_cfdata = cf;
snprintf(dev->dv_xname, sizeof(dev->dv_xname), "%s%d", cd->cd_name,
dev->dv_unit);
printf("%s at %s\n", dev->dv_xname, pci_bus.dv_xname);
ca->ca_attach(&pci_bus, dev, pa);
return 1;
}
}
@ -128,6 +134,8 @@ struct device *config_found_sm(struct device *parent, void *aux, cfprint_t print
dev->dv_unit);
printf("%s at %s\n", dev->dv_xname, parent->dv_xname);
dev->dv_cfdata = cf;
ca->ca_attach(parent, dev, aux);
audio_cd.cd_ndevs = 1;
@ -148,3 +156,15 @@ struct device *device_lookup(struct cfdriver *cd, int unit)
return audio_cd.cd_devs[unit];
}
/*****************
** sys/ucred.h **
*****************/
int suser(struct proc *p)
{
(void)p;
/* we always have special user powers */
return 0;
};

View File

@ -32,7 +32,7 @@
extern struct cfdriver audio_cd;
static dev_t const adev = 0x80; /* audio0 (minor nr 128) */
static dev_t const adev = 0x00; /* audio0 (minor nr 0) */
static dev_t const mdev = 0x10; /* mixer0 (minor nr 16) */
static bool adev_usuable = false;
@ -53,41 +53,23 @@ static bool drv_loaded()
** Dump audio configuration **
******************************/
#define DUMP_INFO(field) \
Genode::log("--- " #field " information ---"); \
Genode::log("sample_rate: ", (unsigned)ai.field.sample_rate); \
Genode::log("channels: ", (unsigned)ai.field.channels); \
Genode::log("precision: ", (unsigned)ai.field.precision); \
Genode::log("bps: ", (unsigned)ai.field.bps); \
Genode::log("encoding: ", (unsigned)ai.field.encoding); \
Genode::log("buffer_size: ", (unsigned)ai.field.buffer_size); \
Genode::log("block_size: ", (unsigned)ai.field.block_size); \
Genode::log("samples: ", (unsigned)ai.field.samples); \
Genode::log("pause: ", (unsigned)ai.field.pause); \
Genode::log("active: ", (unsigned)ai.field.active)
static void dump_pinfo()
static void dump_info()
{
struct audio_info ai;
if (audioioctl(adev, AUDIO_GETINFO, (char*)&ai, 0, 0)) {
struct audio_swpar ap;
AUDIO_INITPAR(&ap);
if (audioioctl(adev, AUDIO_GETPAR, (char*)&ap, 0, 0)) {
Genode::error("could not gather play information");
return;
}
DUMP_INFO(play);
}
static void dump_rinfo()
{
struct audio_info ai;
if (audioioctl(adev, AUDIO_GETINFO, (char*)&ai, 0, 0)) {
Genode::error("could not gather play information");
return;
}
DUMP_INFO(record);
Genode::log("Audio information:");
Genode::log(" sample_rate: ", (unsigned)ap.rate);
Genode::log(" playback channels: ", (unsigned)ap.pchan);
Genode::log(" record channels: ", (unsigned)ap.rchan);
Genode::log(" num blocks: ", (unsigned)ap.nblks);
Genode::log(" block size: ", (unsigned)ap.round);
}
@ -374,36 +356,39 @@ static void configure_mixer(Genode::Env &env, Mixer &mixer, Genode::Xml_node con
static bool configure_audio_device(Genode::Env &env, dev_t dev, Genode::Xml_node config)
{
struct audio_info ai;
struct audio_swpar ap;
int err = audioioctl(adev, AUDIO_GETINFO, (char*)&ai, 0, 0);
AUDIO_INITPAR(&ap);
int err = audioioctl(adev, AUDIO_GETPAR, (char*)&ap, 0, 0);
if (err)
return false;
using namespace Audio;
/* configure the device according to our Audio_out session settings */
ai.play.sample_rate = Audio_out::SAMPLE_RATE;
ai.play.channels = Audio_out::MAX_CHANNELS;
ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
ai.play.block_size = Audio_out::MAX_CHANNELS * sizeof(short) * Audio_out::PERIOD;
/* Configure the device according to our Audio_in session settings
*
* We use Audio_out::MAX_CHANNELS here because the backend provides us
* with two channels that we will mix to one in the front end for now.
/*
* Configure the device according to our Audio_out session parameters.
* Only set the relevant parameters and let the audio(4) subsystem
* figure out the rest.
*/
ai.record.sample_rate = Audio_in::SAMPLE_RATE;
ai.record.channels = Audio_out::MAX_CHANNELS;
ai.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
ai.record.block_size = Audio_out::MAX_CHANNELS * sizeof(short) * Audio_in::PERIOD;
ap.rate = Audio_out::SAMPLE_RATE;
ap.pchan = Audio_out::MAX_CHANNELS;
ap.sig = 1;
ap.bits = 16;
ap.bps = (ap.bits / 8);
ap.round = Audio_out::PERIOD;
/*
* Use 2 blocks, the one that is currently played and the one
* that will be filled in.
*/
ap.nblks = 2;
/*
* For recording use two channels that we will mix to one in the
* front end.
*/
ap.rchan = 2;
err = audioioctl(adev, AUDIO_SETINFO, (char*)&ai, 0, 0);
if (err)
return false;
int fullduplex = 1;
err = audioioctl(adev, AUDIO_SETFD, (char*)&fullduplex, 0, 0);
err = audioioctl(adev, AUDIO_SETPAR, (char*)&ap, 0, 0);
if (err)
return false;
@ -415,8 +400,7 @@ static bool configure_audio_device(Genode::Env &env, dev_t dev, Genode::Xml_node
bool const verbose = config.attribute_value<bool>("verbose", false);
if (verbose) dump_pinfo();
if (verbose) dump_rinfo();
if (verbose) dump_info();
if (verbose) dump_mixer(mixer);
configure_mixer(env, mixer, config);

View File

@ -62,6 +62,7 @@ DUMMY(0, timeout_add_msec)
DUMMY(0, timeout_del)
DUMMY(0, timeout_set)
DUMMY(0, tsleep)
DUMMY(0, tsleep_nsec)
DUMMY(0, vdevgone)
DUMMY(0, device_unref)

View File

@ -47,6 +47,7 @@ typedef unsigned int uint;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
@ -70,6 +71,7 @@ typedef signed long long off_t;
enum {
EIO = 5,
ENXIO = 6,
EBADF = 9,
ENOMEM = 12,
EACCES = 13,
EBUSY = 16,
@ -100,6 +102,7 @@ enum {
M_WAITOK = 0x01,
M_NOWAIT = 0x02,
M_ZERO = 0x08,
M_TEMP = 0x10,
/* types of memory */
M_DEVBUF = 2,
};
@ -120,6 +123,8 @@ enum {
PCATCH = 0x100,
};
#define PAGE_SIZE (1 << 12)
#ifdef __cplusplus
#define NULL 0
#else
@ -128,6 +133,8 @@ enum {
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#define offsetof(s, e) __builtin_offsetof(s, e)
/******************
** sys/kernel.h **
@ -153,6 +160,13 @@ extern int hz;
struct proc { };
/*****************
** sys/ucred.h **
*****************/
int suser(struct proc *p);
/****************
** sys/task.h **
****************/
@ -281,6 +295,8 @@ void mtx_leave(struct mutex *);
** sys/systm.h **
*****************/
#define INFSLP __UINT64_MAX__
extern int nchrdev;
int enodev(void);
@ -296,6 +312,7 @@ void *memset(void *, int, size_t);
void wakeup(const volatile void*);
int tsleep(const volatile void *, int, const char *, int);
int tsleep_nsec(const volatile void *, int, const char *, uint64_t);
int msleep(const volatile void *, struct mutex *, int, const char*, int);
int uiomove(void *, int, struct uio *);
@ -549,6 +566,7 @@ enum {
BUS_DMA_WAITOK = 0x0000,
BUS_DMA_NOWAIT = 0x0001,
BUS_DMA_COHERENT = 0x0004,
BUS_DMA_NOCACHE = 0x0800,
};
@ -615,6 +633,8 @@ enum {
** dev/pci/pcivar.h **
**********************/
#define PCI_FLAGS_MSI_ENABLED 0x20
/* actually from pci_machdep.h */
typedef void *pci_chipset_tag_t;
typedef uint32_t pcitag_t;
@ -625,6 +645,7 @@ struct pci_attach_args
{
bus_dma_tag_t pa_dmat;
pci_chipset_tag_t pa_pc;
int pa_flags;
pcitag_t pa_tag;
pcireg_t pa_id;
pcireg_t pa_class;
@ -676,9 +697,24 @@ int timeout_del(struct timeout *);
** sys/endian.h **
******************/
#define LITTLE_ENDIAN 1234
#define BYTE_ORDER LITTLE_ENDIAN
#define htole32(x) ((uint32_t)(x))
/****************
** sys/time.h **
****************/
struct timeval
{
int64_t tv_sec;
long tv_usec;
};
void microuptime(struct timeval *);
#include <extern_c_end.h>
#endif /* _BSD_EMUL_H_ */

View File

@ -133,7 +133,7 @@ class Bsd::Slab_alloc : public Genode::Slab
static Genode::size_t _calculate_block_size(Genode::size_t object_size)
{
Genode::size_t const block_size = 16*object_size;
Genode::size_t const block_size = 8*object_size;
return Genode::align_addr(block_size, 12);
}
@ -164,7 +164,7 @@ class Bsd::Malloc
enum {
SLAB_START_LOG2 = 5, /* 32 B */
SLAB_STOP_LOG2 = 16, /* 64 KiB */
SLAB_STOP_LOG2 = 17, /* 128 KiB */
NUM_SLABS = (SLAB_STOP_LOG2 - SLAB_START_LOG2) + 1,
};
@ -329,7 +329,7 @@ extern "C" void *malloc(size_t size, int type, int flags)
{
void *addr = malloc_backend().alloc(size);
if (flags & M_ZERO)
if (addr && (flags & M_ZERO))
Genode::memset(addr, 0, size);
return addr;

View File

@ -1,6 +1,7 @@
/*
* \brief Audio driver BSD API emulation
* \author Josef Soentgen
* \author Sebstian Sumpf
* \date 2014-11-09
*/
@ -12,6 +13,7 @@
*/
/* Genode includes */
#include <base/log.h>
#include <base/sleep.h>
#include <base/snprintf.h>
#include <util/string.h>
@ -39,21 +41,381 @@ void mtx_leave(struct mutex *mtx) {
** sys/systm.h **
*****************/
static int _vprintf(char const *format, va_list list)
{
char buf[128] { };
Genode::String_console sc(buf, sizeof(buf));
sc.vprintf(format, list);
return sc.len();
namespace Bsd {
class Console;
class Format_command;
}
/**
* Format string command representation
*/
class Bsd::Format_command
{
public:
enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT,
INVALID };
enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG };
private:
/**
* Read decimal value from string
*/
int decode_decimal(const char *str, int *consumed)
{
int res = 0;
while (1) {
char c = str[*consumed];
if (!c || c < '0' || c > '0' + 9)
return res;
res = (res * 10) + c - '0';
(*consumed)++;
}
}
public:
Type type = INVALID; /* format argument type */
Length length = DEFAULT; /* format argument length */
int padding = 0; /* min number of characters to print */
int base = 10; /* base of numeric arguments */
bool zeropad = false; /* pad with zero instead of space */
bool uppercase = false; /* use upper case for hex numbers */
bool prefix = false; /* prefix with 0x */
int consumed = 0; /* nb of consumed format string chars */
/**
* Constructor
*
* \param format begin of command in format string
*/
explicit Format_command(const char *format)
{
/* check for command begin and eat the character */
if (format[consumed] != '%') return;
if (!format[++consumed]) return;
/* check for %$x syntax */
prefix = (format[consumed] == '#') || (format[consumed] == '.');
if (prefix && !format[++consumed]) return;
/* heading zero indicates zero-padding */
zeropad = (format[consumed] == '0');
/* read decimal padding value */
padding = decode_decimal(format, &consumed);
if (!format[consumed]) return;
/* decode length */
switch (format[consumed]) {
case 'l':
{
/* long long ints are marked by a subsequenting 'l' character */
bool is_long_long = (format[consumed + 1] == 'l');
length = is_long_long ? LONG_LONG : LONG;
consumed += is_long_long ? 2 : 1;
break;
}
case 'z':
case 'Z':
length = SIZE_T;
consumed++;
break;
case 'p':
length = LONG;
break;
default: break;
}
if (!format[consumed]) return;
/* decode type */
switch (format[consumed]) {
case 'd':
case 'i': type = INT; base = 10; break;
case 'o': type = UINT; base = 8; break;
case 'u': type = UINT; base = 10; break;
case 'x': type = UINT; base = 16; break;
case 'X': type = UINT; base = 16; uppercase = 1; break;
case 'p': type = PTR; base = 16; break;
case 'c': type = CHAR; break;
case 's': type = STRING; break;
case '%': type = PERCENT; break;
case 0: return;
default: break;
}
/* eat type character */
consumed++;
if (type != PTR || !format[consumed])
return;
switch (format[consumed]) {
default: return;
}
consumed++;
}
int numeric()
{
return (type == INT || type == UINT || type == PTR);
}
};
/**
* Convert digit to ASCII value
*/
static char ascii(int digit, int uppercase = 0)
{
if (digit > 9)
return digit + (uppercase ? 'A' : 'a') - 10;
return digit + '0';
}
class Bsd::Console
{
private:
enum { BUF_SIZE = 216 };
char _buf[BUF_SIZE + 1];
unsigned _idx = 0;
void _flush()
{
if (!_idx)
return;
_buf[_idx] = 0;
Genode::log(Genode::Cstring(_buf));
_idx = 0;
}
/**
* Output signed value with the specified base
*/
template <typename T>
void _out_signed(T value, unsigned base)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
/* set flag if value is negative */
int neg = value < 0 ? 1 : 0;
/* get absolute value */
value = value < 0 ? -value : value;
int i = 0;
/* handle zero as special case */
if (value == 0)
buf[i++] = ascii(0);
/* fill buffer starting with the least significant digits */
else
for (; value > 0; value /= base)
buf[i++] = ascii(value % base);
/* add sign to buffer for negative values */
if (neg)
_out_char('-');
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
/**
* Output unsigned value with the specified base and padding
*/
template <typename T>
void _out_unsigned(T value, unsigned base, int pad)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
int i = 0;
/* handle zero as special case */
if (value == 0) {
buf[i++] = ascii(0);
pad--;
}
/* fill buffer starting with the least significant digits */
for (; value > 0; value /= base, pad--)
buf[i++] = ascii(value % base);
/* add padding zeros */
for (; pad-- > 0; )
_out_char(ascii(0));
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
protected:
void _out_char(char c)
{
if (c == '\n' || _idx == BUF_SIZE || c == 0)
_flush();
else
_buf[_idx++] = c;
}
void _out_string(const char *str)
{
if (str)
while (*str) _out_char(*str++);
else
_flush();
}
public:
static Console &c()
{
static Console _inst;
return _inst;
}
void vprintf(const char *format, va_list list)
{
while (*format) {
/* eat and output plain characters */
if (*format != '%') {
_out_char(*format++);
continue;
}
/* parse format argument descriptor */
Format_command cmd(format);
/* read numeric argument from va_list */
long long numeric_arg = 0;
if (cmd.numeric()) {
switch (cmd.length) {
case Format_command::LONG_LONG:
numeric_arg = va_arg(list, long long);
break;
case Format_command::LONG:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned long) : va_arg(list, long);
break;
case Format_command::SIZE_T:
numeric_arg = va_arg(list, size_t);
break;
case Format_command::DEFAULT:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned int) : va_arg(list, int);
break;
}
}
/* call type-specific output routines */
switch (cmd.type) {
case Format_command::INT:
if (cmd.length == Format_command::LONG_LONG)
_out_signed<long long>(numeric_arg, cmd.base);
else
_out_signed<long>(numeric_arg, cmd.base);
break;
case Format_command::UINT:
if (cmd.prefix && cmd.base == 16)
_out_string("0x");
if (cmd.length == Format_command::LONG_LONG) {
_out_unsigned<unsigned long long>(numeric_arg, cmd.base, cmd.padding);
break;
}
/* fall through */
case Format_command::PTR:
_out_unsigned<unsigned long>(numeric_arg, cmd.base, cmd.padding);
break;
case Format_command::CHAR:
_out_char(va_arg(list, int));
break;
case Format_command::STRING:
_out_string(va_arg(list, const char *));
break;
case Format_command::PERCENT:
_out_char('%');
break;
case Format_command::INVALID:
_out_string("<warning: unsupported format string argument>");
/* consume the argument of the unsupported command */
va_arg(list, long);
break;
}
/* proceed with format string after command */
format += cmd.consumed;
}
}
};
extern "C" void panic(char const *format, ...)
{
va_list list;
va_start(list, format);
_vprintf(format, list);
Bsd::Console::c().vprintf(format, list);
va_end(list);
Genode::sleep_forever();
@ -65,10 +427,11 @@ extern "C" int printf(const char *format, ...)
va_list list;
va_start(list, format);
int const result = _vprintf(format, list);
Bsd::Console::c().vprintf(format, list);
va_end(list);
return result;
/* hopefully this gets never checked... */
return 0;
}

View File

@ -71,9 +71,9 @@ class Bsd::Timer
millisecs = _timer_conn.elapsed_ms();
}
void delay(Genode::uint64_t ms)
void delay(Genode::uint64_t us)
{
_timer_conn.msleep(ms);
_timer_conn.usleep(us);
}
};
@ -120,6 +120,10 @@ extern "C" int msleep(const volatile void *ident, struct mutex *mtx,
extern "C" void wakeup(const volatile void *ident)
{
if (!_sleep_task) {
Genode::error("sleep task is NULL");
Genode::sleep_forever();
}
_sleep_task->unblock();
_sleep_task = nullptr;
}
@ -133,3 +137,24 @@ extern "C" void delay(int delay)
{
_bsd_timer->delay(delay);
}
/****************
** sys/time.h **
****************/
void microuptime(struct timeval *tv)
{
_bsd_timer->update_millisecs();
if (!tv) { return; }
/*
* So far only needed by auich_calibrate, which
* reuqires microseconds - switching the Bsd::Timer
* implementation over to the new Genode::Timer API
* is probably necessary for that to work properly.
*/
tv->tv_sec = millisecs / 1000;
tv->tv_usec = 0;
}