#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/pcm/sound.h>
#include "feeder_if.h"
static MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
static SLIST_HEAD(, feeder_class) feedertab = SLIST_HEAD_INITIALIZER(feedertab);
void
feeder_register(void *p)
{
struct feeder_class *fc = p;
SLIST_INSERT_HEAD(&feedertab, fc, link);
}
static void
feeder_unregisterall(void *p __unused)
{
SLIST_INIT(&feedertab);
}
static void
feeder_destroy(struct pcm_feeder *f)
{
FEEDER_FREE(f);
kobj_delete((kobj_t)f, M_FEEDER);
}
static struct pcm_feeder *
feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
{
struct pcm_feeder *f;
int err;
f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
if (f == NULL)
return NULL;
f->class = fc;
if (desc != NULL)
f->desc = *desc;
err = FEEDER_INIT(f);
if (err) {
printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
feeder_destroy(f);
return NULL;
}
return f;
}
struct feeder_class *
feeder_getclass(u_int32_t type)
{
struct feeder_class *fc;
SLIST_FOREACH(fc, &feedertab, link) {
if (fc->type == type)
return (fc);
}
return (NULL);
}
int
feeder_add(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
{
struct pcm_feeder *nf;
nf = feeder_create(fc, desc);
if (nf == NULL)
return ENOSPC;
nf->source = c->feeder;
if (c->feeder != NULL)
c->feeder->parent = nf;
c->feeder = nf;
return 0;
}
void
feeder_remove(struct pcm_channel *c)
{
struct pcm_feeder *f;
while (c->feeder != NULL) {
f = c->feeder;
c->feeder = c->feeder->source;
feeder_destroy(f);
}
}
struct pcm_feeder *
feeder_find(struct pcm_channel *c, u_int32_t type)
{
struct pcm_feeder *f;
f = c->feeder;
while (f != NULL) {
if (f->class->type == type)
return f;
f = f->source;
}
return NULL;
}
#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1))
#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2))
#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc))
#define score_chgt(s1, s2) (((s1) & 0xfc) > ((s2) & 0xfc))
#define score_chlt(s1, s2) (((s1) & 0xfc) < ((s2) & 0xfc))
#define score_val(s1) ((s1) & 0x3f00)
#define score_cse(s1) ((s1) & 0x7f)
static u_int32_t
snd_fmtscore(u_int32_t fmt)
{
u_int32_t ret;
ret = 0;
if (fmt & AFMT_SIGNED)
ret |= 1 << 0;
if (fmt & AFMT_BIGENDIAN)
ret |= 1 << 1;
ret |= (AFMT_CHANNEL(fmt) & 0x3f) << 2;
if (fmt & AFMT_A_LAW)
ret |= 1 << 8;
else if (fmt & AFMT_MU_LAW)
ret |= 1 << 9;
else if (fmt & AFMT_8BIT)
ret |= 1 << 10;
else if (fmt & AFMT_16BIT)
ret |= 1 << 11;
else if (fmt & AFMT_24BIT)
ret |= 1 << 12;
else if (fmt & AFMT_32BIT)
ret |= 1 << 13;
return ret;
}
static u_int32_t
snd_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
{
u_int32_t best, score, score2, oldscore;
int i;
if (fmt == 0 || fmts == NULL || fmts[0] == 0)
return 0;
if (snd_fmtvalid(fmt, fmts))
return fmt;
best = 0;
score = snd_fmtscore(fmt);
oldscore = 0;
for (i = 0; fmts[i] != 0; i++) {
score2 = snd_fmtscore(fmts[i]);
if (cheq && !score_cheq(score, score2) &&
(score_chlt(score2, score) ||
(oldscore != 0 && score_chgt(score2, oldscore))))
continue;
if (oldscore == 0 ||
(score_val(score2) == score_val(score)) ||
(score_val(score2) == score_val(oldscore)) ||
(score_val(score2) > score_val(oldscore) &&
score_val(score2) < score_val(score)) ||
(score_val(score2) < score_val(oldscore) &&
score_val(score2) > score_val(score)) ||
(score_val(oldscore) < score_val(score) &&
score_val(score2) > score_val(oldscore))) {
if (score_val(oldscore) != score_val(score2) ||
score_cse(score) == score_cse(score2) ||
((score_cse(oldscore) != score_cse(score) &&
!score_endianeq(score, oldscore) &&
(score_endianeq(score, score2) ||
(!score_signeq(score, oldscore) &&
score_signeq(score, score2)))))) {
best = fmts[i];
oldscore = score2;
}
}
}
return best;
}
static u_int32_t
snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
{
return snd_fmtbestfunc(fmt, fmts, 0);
}
static u_int32_t
snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts)
{
return snd_fmtbestfunc(fmt, fmts, 1);
}
u_int32_t
snd_fmtbest(u_int32_t fmt, u_int32_t *fmts)
{
u_int32_t best1, best2;
u_int32_t score, score1, score2;
if (snd_fmtvalid(fmt, fmts))
return fmt;
best1 = snd_fmtbestchannel(fmt, fmts);
best2 = snd_fmtbestbit(fmt, fmts);
if (best1 != 0 && best2 != 0 && best1 != best2) {
if (AFMT_CHANNEL(fmt) > 1)
return best1;
else {
score = score_val(snd_fmtscore(fmt));
score1 = score_val(snd_fmtscore(best1));
score2 = score_val(snd_fmtscore(best2));
if (score1 == score2 || score1 == score)
return best1;
else if (score2 == score)
return best2;
else if (score1 > score2)
return best1;
return best2;
}
} else if (best2 == 0)
return best1;
else
return best2;
}
static int
feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
{
struct snd_dbuf *src = source;
int l, offset;
KASSERT(count > 0, ("feed_root: count == 0"));
if (++ch->feedcount == 0)
ch->feedcount = 2;
l = min(count, sndbuf_getready(src));
if (ch->direction == PCMDIR_REC) {
sndbuf_dispose(src, buffer, l);
return l;
}
offset = count - l;
if (offset > 0) {
if (snd_verbose > 3)
printf("%s: (%s) %spending %d bytes "
"(count=%d l=%d feed=%d)\n",
__func__,
(ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
(ch->feedcount == 1) ? "pre" : "ap",
offset, count, l, ch->feedcount);
if (ch->feedcount == 1) {
memset(buffer, sndbuf_zerodata(src->fmt), offset);
if (l > 0)
sndbuf_dispose(src, buffer + offset, l);
else
ch->feedcount--;
} else {
if (l > 0)
sndbuf_dispose(src, buffer, l);
memset(buffer + l, sndbuf_zerodata(src->fmt), offset);
if (!(ch->flags & CHN_F_CLOSING))
ch->xruns++;
}
} else if (l > 0)
sndbuf_dispose(src, buffer, l);
return count;
}
static kobj_method_t feeder_root_methods[] = {
KOBJMETHOD(feeder_feed, feed_root),
KOBJMETHOD_END
};
static struct feeder_class feeder_root_class = {
.name = "feeder_root",
.methods = feeder_root_methods,
.size = sizeof(struct pcm_feeder),
.type = FEEDER_ROOT,
};
SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register,
&feeder_root_class);
SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);