feeder.c revision 165835
1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg */ 2650724Scg 2753465Scg#include <dev/sound/pcm/sound.h> 2850724Scg 2970134Scg#include "feeder_if.h" 3070134Scg 3182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 165835 2007-01-06 19:11:48Z netchild $"); 3282180Scg 3370134ScgMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 3470134Scg 3566308Scg#define MAXFEEDERS 256 3665645Scg#undef FEEDER_DEBUG 3750724Scg 38164614Sariffint feeder_buffersize = FEEDBUFSZ; 39164614SariffTUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); 40164614SariffSYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, 41164614Sariff &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); 42164614Sariff 4364881Scgstruct feedertab_entry { 4464881Scg SLIST_ENTRY(feedertab_entry) link; 4570134Scg struct feeder_class *feederclass; 4664881Scg struct pcm_feederdesc *desc; 4764881Scg 4864881Scg int idx; 4964881Scg}; 5064881Scgstatic SLIST_HEAD(, feedertab_entry) feedertab; 5164881Scg 5250724Scg/*****************************************************************************/ 5350724Scg 5464881Scgvoid 5564881Scgfeeder_register(void *p) 5664881Scg{ 5789834Scg static int feedercnt = 0; 5889834Scg 5970134Scg struct feeder_class *fc = p; 6064881Scg struct feedertab_entry *fte; 6164881Scg int i; 6264881Scg 6364881Scg if (feedercnt == 0) { 6489834Scg KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 6589834Scg 6664881Scg SLIST_INIT(&feedertab); 67111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 6889834Scg if (fte == NULL) { 6997274Sbde printf("can't allocate memory for root feeder: %s\n", 7097274Sbde fc->name); 7189834Scg 7289834Scg return; 7389834Scg } 7470134Scg fte->feederclass = fc; 7564881Scg fte->desc = NULL; 7664881Scg fte->idx = feedercnt; 7764881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 7864881Scg feedercnt++; 7989834Scg 80164614Sariff /* initialize global variables */ 81164614Sariff 82164614Sariff if (snd_verbose < 0 || snd_verbose > 3) 83164614Sariff snd_verbose = 1; 84164614Sariff 85164614Sariff if (snd_unit < 0 || snd_unit > PCMMAXDEV) 86164614Sariff snd_unit = 0; 87164614Sariff 88164614Sariff if (snd_maxautovchans < 0 || 89164614Sariff snd_maxautovchans > SND_MAXVCHANS) 90164614Sariff snd_maxautovchans = 0; 91164614Sariff 92164614Sariff if (chn_latency < CHN_LATENCY_MIN || 93164614Sariff chn_latency > CHN_LATENCY_MAX) 94164614Sariff chn_latency = CHN_LATENCY_DEFAULT; 95164614Sariff 96164614Sariff if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || 97164614Sariff chn_latency_profile > CHN_LATENCY_PROFILE_MAX) 98164614Sariff chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 99164614Sariff 100164614Sariff if (feeder_buffersize < FEEDBUFSZ_MIN || 101164614Sariff feeder_buffersize > FEEDBUFSZ_MAX) 102164614Sariff feeder_buffersize = FEEDBUFSZ; 103164614Sariff 104164614Sariff if (feeder_rate_min < FEEDRATE_MIN || 105164614Sariff feeder_rate_max < FEEDRATE_MIN || 106164614Sariff feeder_rate_min > FEEDRATE_MAX || 107164614Sariff feeder_rate_max > FEEDRATE_MAX || 108164614Sariff !(feeder_rate_min < feeder_rate_max)) { 109164614Sariff feeder_rate_min = FEEDRATE_RATEMIN; 110164614Sariff feeder_rate_max = FEEDRATE_RATEMAX; 111164614Sariff } 112164614Sariff 113164614Sariff if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || 114164614Sariff feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) 115164614Sariff feeder_rate_round = FEEDRATE_ROUNDHZ; 116164614Sariff 117164614Sariff if (bootverbose) 118164614Sariff printf("%s: snd_unit=%d snd_maxautovchans=%d " 119164614Sariff "latency=%d feeder_buffersize=%d " 120164614Sariff "feeder_rate_min=%d feeder_rate_max=%d " 121164614Sariff "feeder_rate_round=%d\n", 122164614Sariff __func__, snd_unit, snd_maxautovchans, 123164614Sariff chn_latency, feeder_buffersize, 124164614Sariff feeder_rate_min, feeder_rate_max, 125164614Sariff feeder_rate_round); 126164614Sariff 12789834Scg /* we've got our root feeder so don't veto pcm loading anymore */ 12889834Scg pcm_veto_load = 0; 12989834Scg 13064881Scg return; 13164881Scg } 13264881Scg 13389834Scg KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 13489834Scg 13589834Scg /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 13664881Scg i = 0; 13770134Scg while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 13875355Scg /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 139111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 14089834Scg if (fte == NULL) { 14189834Scg printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 14289834Scg 14389834Scg return; 14489834Scg } 14570134Scg fte->feederclass = fc; 14670134Scg fte->desc = &fc->desc[i]; 14764881Scg fte->idx = feedercnt; 14864881Scg fte->desc->idx = feedercnt; 14964881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 15064881Scg i++; 15164881Scg } 15264881Scg feedercnt++; 15364881Scg if (feedercnt >= MAXFEEDERS) 15489834Scg printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 15564881Scg} 15664881Scg 15774363Scgstatic void 15874363Scgfeeder_unregisterall(void *p) 15974363Scg{ 16074363Scg struct feedertab_entry *fte, *next; 16174363Scg 16274363Scg next = SLIST_FIRST(&feedertab); 16374363Scg while (next != NULL) { 16474363Scg fte = next; 16574363Scg next = SLIST_NEXT(fte, link); 16674363Scg free(fte, M_FEEDER); 16774363Scg } 16874363Scg} 16974363Scg 17050724Scgstatic int 17164881Scgcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 17250724Scg{ 17366308Scg return ((n->type == m->type) && 17466308Scg ((n->in == 0) || (n->in == m->in)) && 17566308Scg ((n->out == 0) || (n->out == m->out)) && 17666308Scg (n->flags == m->flags)); 17750724Scg} 17850724Scg 17970134Scgstatic void 18074763Scgfeeder_destroy(struct pcm_feeder *f) 18150724Scg{ 18270134Scg FEEDER_FREE(f); 18370134Scg kobj_delete((kobj_t)f, M_FEEDER); 18470134Scg} 18564881Scg 18674763Scgstatic struct pcm_feeder * 18770134Scgfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 18870134Scg{ 18974763Scg struct pcm_feeder *f; 19070134Scg int err; 19170134Scg 192111909Sorion f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); 19389834Scg if (f == NULL) 19489834Scg return NULL; 19589834Scg 19670134Scg f->align = fc->align; 19789834Scg f->data = fc->data; 19889834Scg f->source = NULL; 19989834Scg f->parent = NULL; 20089834Scg f->class = fc; 20189834Scg f->desc = &(f->desc_static); 20289834Scg 20389834Scg if (desc) { 20470134Scg *(f->desc) = *desc; 20589834Scg } else { 20670134Scg f->desc->type = FEEDER_ROOT; 20770134Scg f->desc->in = 0; 20870134Scg f->desc->out = 0; 20970134Scg f->desc->flags = 0; 21070134Scg f->desc->idx = 0; 21164881Scg } 21289834Scg 21370134Scg err = FEEDER_INIT(f); 21470134Scg if (err) { 21575319Scg printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 21670134Scg feeder_destroy(f); 21789834Scg 21870134Scg return NULL; 21989834Scg } 22089834Scg 22189834Scg return f; 22250724Scg} 22350724Scg 22470134Scgstruct feeder_class * 22570134Scgfeeder_getclass(struct pcm_feederdesc *desc) 22650724Scg{ 22764881Scg struct feedertab_entry *fte; 22850724Scg 22964881Scg SLIST_FOREACH(fte, &feedertab, link) { 23070134Scg if ((desc == NULL) && (fte->desc == NULL)) 23170134Scg return fte->feederclass; 23270134Scg if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 23370134Scg return fte->feederclass; 23464881Scg } 23564881Scg return NULL; 23650724Scg} 23750724Scg 23850724Scgint 23974763Scgchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 24066308Scg{ 24174763Scg struct pcm_feeder *nf; 24270134Scg 24370134Scg nf = feeder_create(fc, desc); 24470134Scg if (nf == NULL) 24589834Scg return ENOSPC; 24666308Scg 24770134Scg nf->source = c->feeder; 24866308Scg 249117307Scg /* XXX we should use the lowest common denominator for align */ 25066308Scg if (nf->align > 0) 25166308Scg c->align += nf->align; 25266308Scg else if (nf->align < 0 && c->align < -nf->align) 25366308Scg c->align = -nf->align; 254117307Scg if (c->feeder != NULL) 255117307Scg c->feeder->parent = nf; 25666308Scg c->feeder = nf; 25766308Scg 25866308Scg return 0; 25966308Scg} 26066308Scg 26166308Scgint 26274763Scgchn_removefeeder(struct pcm_channel *c) 26350724Scg{ 26474763Scg struct pcm_feeder *f; 26550724Scg 26670134Scg if (c->feeder == NULL) 26764881Scg return -1; 26870134Scg f = c->feeder; 26970134Scg c->feeder = c->feeder->source; 27070134Scg feeder_destroy(f); 27189834Scg 27264881Scg return 0; 27350724Scg} 27450724Scg 27574763Scgstruct pcm_feeder * 27674763Scgchn_findfeeder(struct pcm_channel *c, u_int32_t type) 27766308Scg{ 27874763Scg struct pcm_feeder *f; 27966308Scg 28066308Scg f = c->feeder; 28166308Scg while (f != NULL) { 28266308Scg if (f->desc->type == type) 28366308Scg return f; 28466308Scg f = f->source; 28566308Scg } 28689834Scg 28766308Scg return NULL; 28866308Scg} 28966308Scg 29050724Scgstatic int 29174763Scgchainok(struct pcm_feeder *test, struct pcm_feeder *stop) 29250724Scg{ 29364881Scg u_int32_t visited[MAXFEEDERS / 32]; 29464881Scg u_int32_t idx, mask; 29564881Scg 29664881Scg bzero(visited, sizeof(visited)); 29764881Scg while (test && (test != stop)) { 29864881Scg idx = test->desc->idx; 29964881Scg if (idx < 0) 30064881Scg panic("bad idx %d", idx); 30164881Scg if (idx >= MAXFEEDERS) 30264881Scg panic("bad idx %d", idx); 30364881Scg mask = 1 << (idx & 31); 30464881Scg idx >>= 5; 30564881Scg if (visited[idx] & mask) 30664881Scg return 0; 30764881Scg visited[idx] |= mask; 30864881Scg test = test->source; 30964881Scg } 31089834Scg 31164881Scg return 1; 31250724Scg} 31350724Scg 314164614Sariff/* 315164614Sariff * See feeder_fmtchain() for the mumbo-jumbo ridiculous explaination 316164614Sariff * of what the heck is this FMT_Q_* 317164614Sariff */ 318164614Sariff#define FMT_Q_UP 1 319164614Sariff#define FMT_Q_DOWN 2 320164614Sariff#define FMT_Q_EQ 3 321164614Sariff#define FMT_Q_MULTI 4 32264881Scg 323164614Sariff/* 324164614Sariff * 14bit format scoring 325164614Sariff * -------------------- 326164614Sariff * 327164614Sariff * 13 12 11 10 9 8 2 1 0 offset 328164614Sariff * +---+---+---+---+---+---+-------------+---+---+ 329164614Sariff * | X | X | X | X | X | X | X X X X X X | X | X | 330164614Sariff * +---+---+---+---+---+---+-------------+---+---+ 331164614Sariff * | | | | | | | | | 332164614Sariff * | | | | | | | | +--> signed? 333164614Sariff * | | | | | | | | 334164614Sariff * | | | | | | | +------> bigendian? 335164614Sariff * | | | | | | | 336164614Sariff * | | | | | | +---------------> total channels 337164614Sariff * | | | | | | 338164614Sariff * | | | | | +------------------------> AFMT_A_LAW 339164614Sariff * | | | | | 340164614Sariff * | | | | +----------------------------> AFMT_MU_LAW 341164614Sariff * | | | | 342164614Sariff * | | | +--------------------------------> AFMT_8BIT 343164614Sariff * | | | 344164614Sariff * | | +------------------------------------> AFMT_16BIT 345164614Sariff * | | 346164614Sariff * | +----------------------------------------> AFMT_24BIT 347164614Sariff * | 348164614Sariff * +--------------------------------------------> AFMT_32BIT 349164614Sariff */ 350164614Sariff#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) 351164614Sariff#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) 352164614Sariff#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) 353164614Sariff#define score_val(s1) ((s1) & 0x3f00) 354164614Sariff#define score_cse(s1) ((s1) & 0x7f) 35564881Scg 356164614Sariffu_int32_t 357154684Sariffchn_fmtscore(u_int32_t fmt) 358154684Sariff{ 359164614Sariff u_int32_t ret; 360164614Sariff 361164614Sariff ret = 0; 362164614Sariff if (fmt & AFMT_SIGNED) 363164614Sariff ret |= 1 << 0; 364164614Sariff if (fmt & AFMT_BIGENDIAN) 365164614Sariff ret |= 1 << 1; 366164614Sariff if (fmt & AFMT_STEREO) 367164614Sariff ret |= (2 & 0x3f) << 2; 368164614Sariff else 369164614Sariff ret |= (1 & 0x3f) << 2; 370154969Sariff if (fmt & AFMT_A_LAW) 371164614Sariff ret |= 1 << 8; 372164614Sariff else if (fmt & AFMT_MU_LAW) 373164614Sariff ret |= 1 << 9; 374164614Sariff else if (fmt & AFMT_8BIT) 375164614Sariff ret |= 1 << 10; 376164614Sariff else if (fmt & AFMT_16BIT) 377164614Sariff ret |= 1 << 11; 378164614Sariff else if (fmt & AFMT_24BIT) 379164614Sariff ret |= 1 << 12; 380164614Sariff else if (fmt & AFMT_32BIT) 381164614Sariff ret |= 1 << 13; 382164614Sariff 383164614Sariff return ret; 384154684Sariff} 385154684Sariff 386164614Sariffstatic u_int32_t 387164614Sariffchn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) 388154684Sariff{ 389164614Sariff u_int32_t best, score, score2, oldscore; 390164614Sariff int i; 391154684Sariff 392164614Sariff if (fmt == 0 || fmts == NULL || fmts[0] == 0) 393164614Sariff return 0; 394164614Sariff 395164614Sariff if (fmtvalid(fmt, fmts)) 396164614Sariff return fmt; 397164614Sariff 398154684Sariff best = 0; 399154684Sariff score = chn_fmtscore(fmt); 400154684Sariff oldscore = 0; 401154684Sariff for (i = 0; fmts[i] != 0; i++) { 402154684Sariff score2 = chn_fmtscore(fmts[i]); 403164614Sariff if (cheq && !score_cheq(score, score2)) 404164614Sariff continue; 405164614Sariff if (oldscore == 0 || 406164614Sariff (score_val(score2) == score_val(score)) || 407164614Sariff (score_val(score2) == score_val(oldscore)) || 408164614Sariff (score_val(score2) > score_val(oldscore) && 409164614Sariff score_val(score2) < score_val(score)) || 410164614Sariff (score_val(score2) < score_val(oldscore) && 411164614Sariff score_val(score2) > score_val(score)) || 412164614Sariff (score_val(oldscore) < score_val(score) && 413164614Sariff score_val(score2) > score_val(oldscore))) { 414164614Sariff if (score_val(oldscore) != score_val(score2) || 415164614Sariff score_cse(score) == score_cse(score2) || 416164614Sariff ((score_cse(oldscore) != score_cse(score) && 417164614Sariff !score_endianeq(score, oldscore) && 418164614Sariff (score_endianeq(score, score2) || 419164614Sariff (!score_signeq(score, oldscore) && 420164614Sariff score_signeq(score, score2)))))) { 421164614Sariff best = fmts[i]; 422164614Sariff oldscore = score2; 423164614Sariff } 424154684Sariff } 425154684Sariff } 426154684Sariff return best; 427154684Sariff} 428154684Sariff 429154684Sariffu_int32_t 430164614Sariffchn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) 431164614Sariff{ 432164614Sariff return chn_fmtbestfunc(fmt, fmts, 0); 433164614Sariff} 434164614Sariff 435164614Sariffu_int32_t 436154684Sariffchn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) 437154684Sariff{ 438164614Sariff return chn_fmtbestfunc(fmt, fmts, 1); 439154684Sariff} 440154684Sariff 441154684Sariffu_int32_t 442154684Sariffchn_fmtbest(u_int32_t fmt, u_int32_t *fmts) 443154684Sariff{ 444154684Sariff u_int32_t best1, best2; 445164614Sariff u_int32_t score, score1, score2; 446154684Sariff 447164614Sariff if (fmtvalid(fmt, fmts)) 448164614Sariff return fmt; 449164614Sariff 450154684Sariff best1 = chn_fmtbeststereo(fmt, fmts); 451154684Sariff best2 = chn_fmtbestbit(fmt, fmts); 452154684Sariff 453164614Sariff if (best1 != 0 && best2 != 0 && best1 != best2) { 454154684Sariff if (fmt & AFMT_STEREO) 455154684Sariff return best1; 456154684Sariff else { 457164614Sariff score = score_val(chn_fmtscore(fmt)); 458164614Sariff score1 = score_val(chn_fmtscore(best1)); 459164614Sariff score2 = score_val(chn_fmtscore(best2)); 460154969Sariff if (score1 == score2 || score1 == score) 461154969Sariff return best1; 462154969Sariff else if (score2 == score) 463154684Sariff return best2; 464154969Sariff else if (score1 > score2) 465154684Sariff return best1; 466154684Sariff return best2; 467154684Sariff } 468154684Sariff } else if (best2 == 0) 469154684Sariff return best1; 470155958Sjhb else 471154684Sariff return best2; 472154684Sariff} 473154684Sariff 474164614Sariffstatic struct pcm_feeder * 475164614Sarifffeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 476164614Sariff{ 477164614Sariff struct feedertab_entry *fte, *ftebest; 478164614Sariff struct pcm_feeder *try, *ret; 479164614Sariff uint32_t fl, qout, qsrc, qdst; 480164614Sariff int qtype; 481164614Sariff 482164614Sariff if (to == NULL || to[0] == 0) 483164614Sariff return NULL; 484164614Sariff 485164614Sariff DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); 486164614Sariff if (fmtvalid(source->desc->out, to)) { 487164614Sariff DEB(printf("got it\n")); 488164614Sariff return source; 489164614Sariff } 490164614Sariff 491164614Sariff if (maxdepth < 0) 492164614Sariff return NULL; 493164614Sariff 494164614Sariff /* 495164614Sariff * WARNING: THIS IS _NOT_ FOR THE FAINT HEART 496164614Sariff * Disclaimer: I don't expect anybody could understand this 497164614Sariff * without deep logical and mathematical analysis 498164614Sariff * involving various unnamed probability theorem. 499164614Sariff * 500164614Sariff * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm 501164614Sariff * is **extremely** difficult to digest especially when applied to 502164614Sariff * large sets / numbers of random chains (feeders), each with 503164614Sariff * unique characteristic providing different sets of in/out format. 504164614Sariff * 505164614Sariff * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: 506164614Sariff * 1) Format chains 507164614Sariff * 1.1 "8bit to any, not to 8bit" 508164614Sariff * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] 509164614Sariff * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] 510164614Sariff * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] 511164614Sariff * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] 512164614Sariff * 1.2 "Any to 8bit, not from 8bit" 513164614Sariff * 1.2.1 sign can remain consistent, e.g: s16le -> s8 514164614Sariff * 1.2.2 sign can be changed, e.g: s16le -> u8 515164614Sariff * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 516164614Sariff * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 517164614Sariff * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" 518164614Sariff * 1.3.1 endian MUST remain consistent 519164614Sariff * 1.3.2 sign CAN be changed 520164614Sariff * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding 521164614Sariff * 16bit to 24bit . 522164614Sariff * 2) Channel chains (mono <-> stereo) 523164614Sariff * 2.1 Both endian and sign MUST remain consistent 524164614Sariff * 3) Endian chains (big endian <-> little endian) 525164614Sariff * 3.1 Channels and sign MUST remain consistent 526164614Sariff * 4) Sign chains (signed <-> unsigned) 527164614Sariff * 4.1 Channels and endian MUST remain consistent 528164614Sariff * 529164614Sariff * .. and the mother of all chaining rules: 530164614Sariff * 531164614Sariff * Rules 0: Source and destination MUST not contain multiple selections. 532164614Sariff * (qtype != FMT_Q_MULTI) 533164614Sariff * 534164614Sariff * First of all, our caller ( chn_fmtchain() ) will reduce the possible 535164614Sariff * multiple from/to formats to a single best format using chn_fmtbest(). 536164614Sariff * Then, using chn_fmtscore(), we determine the chaining characteristic. 537164614Sariff * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining 538164614Sariff * type while still adhering above chaining rules. 539164614Sariff * 540164614Sariff * The need for this complicated chaining procedures is inevitable, 541164614Sariff * since currently we have more than 200 different types of FEEDER_FMT 542164614Sariff * doing various unique format conversion. Without this (the old way), 543164614Sariff * it is possible to generate broken chain since it doesn't do any 544164614Sariff * sanity checking to ensure that the output format is "properly aligned" 545164614Sariff * with the direction of conversion (quality up/down/equal). 546164614Sariff * 547164614Sariff * Conversion: s24le to s32le 548164614Sariff * Possible chain: 1) s24le -> s32le (correct, optimized) 549164614Sariff * 2) s24le -> s16le -> s32le 550164614Sariff * (since we have feeder_24to16 and feeder_16to32) 551164614Sariff * +-- obviously broken! 552164614Sariff * 553164614Sariff * Using scoring mechanisme, this will ensure that the chaining 554164614Sariff * process do the right thing, or at least, give the best chain 555164614Sariff * possible without causing quality (the 'Q') degradation. 556164614Sariff */ 557164614Sariff 558164614Sariff qdst = chn_fmtscore(to[0]); 559164614Sariff qsrc = chn_fmtscore(source->desc->out); 560164614Sariff 561164614Sariff#define score_q(s1) score_val(s1) 562164614Sariff#define score_8bit(s1) ((s1) & 0x700) 563164614Sariff#define score_non8bit(s1) (!score_8bit(s1)) 564164614Sariff#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ 565164614Sariff (score_8bit(s2) && score_non8bit(s1))) 566164614Sariff 567164614Sariff#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) 568164614Sariff#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) 569164614Sariff#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) 570164614Sariff#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ 571164614Sariff 0x2 : 0x0)) 572164614Sariff#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) 573164614Sariff#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ 574164614Sariff ((score_cheq(s1, s2) && \ 575164614Sariff score_endianeq(s1, s2)) ? \ 576164614Sariff 0x1 : 0x0) | \ 577164614Sariff ((score_cheq(s1, s2) && \ 578164614Sariff score_signeq(s1, s2)) ? \ 579164614Sariff 0x2 : 0x0)) 580164614Sariff 581164614Sariff /* Determine chaining direction and set matching flag */ 582164614Sariff fl = 0x3fff; 583164614Sariff if (to[1] != 0) { 584164614Sariff qtype = FMT_Q_MULTI; 585164614Sariff printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); 586164614Sariff } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { 587164614Sariff qtype = FMT_Q_DOWN; 588164614Sariff fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); 589164614Sariff } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { 590164614Sariff qtype = FMT_Q_UP; 591164614Sariff fl = FMT_Q_UP_FLAGS(qsrc, qdst); 592164614Sariff } else { 593164614Sariff qtype = FMT_Q_EQ; 594164614Sariff fl = FMT_Q_EQ_FLAGS(qsrc, qdst); 595164614Sariff } 596164614Sariff 597164614Sariff ftebest = NULL; 598164614Sariff 599164614Sariff SLIST_FOREACH(fte, &feedertab, link) { 600164614Sariff if (fte->desc == NULL) 601164614Sariff continue; 602164614Sariff if (fte->desc->type != FEEDER_FMT) 603164614Sariff continue; 604164614Sariff qout = chn_fmtscore(fte->desc->out); 605164614Sariff#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) 606164614Sariff#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) 607164614Sariff#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ 608164614Sariff score_q(s3) >= score_q(s1) && \ 609164614Sariff score_q(s3) <= score_q(s2)) 610164614Sariff#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ 611164614Sariff score_q(s3) <= score_q(s1) && \ 612164614Sariff score_q(s3) >= score_q(s2)) 613164614Sariff#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ 614164614Sariff score_q(s1) == score_q(s2)) 615164614Sariff if (fte->desc->in == source->desc->out && 616164614Sariff (FMT_Q_MULTI_VALIDATE(qtype) || 617164614Sariff (FMT_Q_FL_MATCH(fl, qout, qdst) && 618164614Sariff (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || 619164614Sariff FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || 620164614Sariff FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { 621164614Sariff try = feeder_create(fte->feederclass, fte->desc); 622164614Sariff if (try) { 623164614Sariff try->source = source; 624164614Sariff ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 625164614Sariff if (ret != NULL) 626164614Sariff return ret; 627164614Sariff feeder_destroy(try); 628164614Sariff } 629164614Sariff } else if (fte->desc->in == source->desc->out) { 630164614Sariff /* XXX quality must be considered! */ 631164614Sariff if (ftebest == NULL) 632164614Sariff ftebest = fte; 633164614Sariff } 634164614Sariff } 635164614Sariff 636164614Sariff if (ftebest != NULL) { 637164614Sariff try = feeder_create(ftebest->feederclass, ftebest->desc); 638164614Sariff if (try) { 639164614Sariff try->source = source; 640164614Sariff ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 641164614Sariff if (ret != NULL) 642164614Sariff return ret; 643164614Sariff feeder_destroy(try); 644164614Sariff } 645164614Sariff } 646164614Sariff 647164614Sariff /* printf("giving up %s...\n", source->class->name); */ 648164614Sariff 649164614Sariff return NULL; 650164614Sariff} 651164614Sariff 652154684Sariffu_int32_t 65374763Scgchn_fmtchain(struct pcm_channel *c, u_int32_t *to) 65464881Scg{ 65589686Scg struct pcm_feeder *try, *del, *stop; 656154684Sariff u_int32_t tmpfrom[2], tmpto[2], best, *from; 65789686Scg int i, max, bestmax; 65864881Scg 65989686Scg KASSERT(c != NULL, ("c == NULL")); 66089686Scg KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 66189686Scg KASSERT(to != NULL, ("to == NULL")); 66289686Scg KASSERT(to[0] != 0, ("to[0] == 0")); 66389686Scg 664164614Sariff if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) 665164614Sariff return 0; 666164614Sariff 66764881Scg stop = c->feeder; 668164614Sariff best = 0; 66989686Scg 67089686Scg if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 67189686Scg from = chn_getcaps(c)->fmtlist; 672164614Sariff if (from[1] != 0) { 673154684Sariff best = chn_fmtbest(to[0], from); 674154684Sariff if (best != 0) { 675154684Sariff tmpfrom[0] = best; 676154684Sariff tmpfrom[1] = 0; 677154684Sariff from = tmpfrom; 678154684Sariff } 679154684Sariff } 68089686Scg } else { 68189686Scg tmpfrom[0] = c->feeder->desc->out; 68289686Scg tmpfrom[1] = 0; 68389686Scg from = tmpfrom; 684154684Sariff if (to[1] != 0) { 685164614Sariff best = chn_fmtbest(from[0], to); 686164614Sariff if (best != 0) { 687164614Sariff tmpto[0] = best; 688154684Sariff tmpto[1] = 0; 689154684Sariff to = tmpto; 690154684Sariff } 691154684Sariff } 69265645Scg } 69389686Scg 694164614Sariff#define FEEDER_FMTCHAIN_MAXDEPTH 8 695164614Sariff 696164614Sariff try = NULL; 697164614Sariff 698164614Sariff if (to[0] != 0 && from[0] != 0 && 699164614Sariff to[1] == 0 && from[1] == 0) { 70089686Scg max = 0; 701164614Sariff best = from[0]; 702164614Sariff c->feeder->desc->out = best; 703164614Sariff do { 70489686Scg try = feeder_fmtchain(to, c->feeder, stop, max); 705164614Sariff DEB(if (try != NULL) { 706164614Sariff printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", 707165835Snetchild __func__, from[0], to[0], max); 708164614Sariff }); 709164614Sariff } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); 710164614Sariff } else { 711164614Sariff printf("%s: Using the old-way format chaining!\n", __func__); 712164614Sariff i = 0; 713164614Sariff best = 0; 714164614Sariff bestmax = 100; 715164614Sariff while (from[i] != 0) { 716164614Sariff c->feeder->desc->out = from[i]; 717164614Sariff try = NULL; 718164614Sariff max = 0; 719164614Sariff do { 720164614Sariff try = feeder_fmtchain(to, c->feeder, stop, max); 721164614Sariff } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); 722164614Sariff if (try != NULL && max < bestmax) { 723164614Sariff bestmax = max; 724164614Sariff best = from[i]; 725164614Sariff } 726164614Sariff while (try != NULL && try != stop) { 727164614Sariff del = try; 728164614Sariff try = try->source; 729164614Sariff feeder_destroy(del); 730164614Sariff } 731164614Sariff i++; 73289686Scg } 733164614Sariff if (best == 0) 734164614Sariff return 0; 735164614Sariff 736164614Sariff c->feeder->desc->out = best; 737164614Sariff try = feeder_fmtchain(to, c->feeder, stop, bestmax); 73889686Scg } 73964881Scg if (try == NULL) 74064881Scg return 0; 74189686Scg 74264881Scg c->feeder = try; 74364881Scg c->align = 0; 74465645Scg#ifdef FEEDER_DEBUG 74589686Scg printf("\n\nchain: "); 74665645Scg#endif 74764881Scg while (try && (try != stop)) { 74865645Scg#ifdef FEEDER_DEBUG 74975319Scg printf("%s [%d]", try->class->name, try->desc->idx); 75065645Scg if (try->source) 75165645Scg printf(" -> "); 75265645Scg#endif 75389686Scg if (try->source) 75489686Scg try->source->parent = try; 75564881Scg if (try->align > 0) 75664881Scg c->align += try->align; 75764881Scg else if (try->align < 0 && c->align < -try->align) 75864881Scg c->align = -try->align; 75964881Scg try = try->source; 76064881Scg } 76165645Scg#ifdef FEEDER_DEBUG 76275319Scg printf("%s [%d]\n", try->class->name, try->desc->idx); 76365645Scg#endif 76489834Scg 765149950Snetchild if (c->direction == PCMDIR_REC) { 766149950Snetchild try = c->feeder; 767149950Snetchild while (try != NULL) { 768149950Snetchild if (try->desc->type == FEEDER_ROOT) 769149950Snetchild return try->desc->out; 770149950Snetchild try = try->source; 771149950Snetchild } 772149950Snetchild return best; 773149950Snetchild } else 774149950Snetchild return c->feeder->desc->out; 77564881Scg} 77666308Scg 777117307Scgvoid 778117307Scgfeeder_printchain(struct pcm_feeder *head) 779117307Scg{ 780117307Scg struct pcm_feeder *f; 781117307Scg 782117307Scg printf("feeder chain (head @%p)\n", head); 783117307Scg f = head; 784117307Scg while (f != NULL) { 785117307Scg printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); 786117307Scg f = f->source; 787117307Scg } 788117307Scg printf("[end]\n\n"); 789117307Scg} 790117307Scg 79166308Scg/*****************************************************************************/ 79266308Scg 79366308Scgstatic int 79474763Scgfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 79566308Scg{ 79674763Scg struct snd_dbuf *src = source; 797164614Sariff int l, offset; 79866308Scg 79974763Scg KASSERT(count > 0, ("feed_root: count == 0")); 80074763Scg /* count &= ~((1 << ch->align) - 1); */ 80174763Scg KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 80266308Scg 803164614Sariff if (++ch->feedcount == 0) 804164614Sariff ch->feedcount = 2; 805164614Sariff 80674763Scg l = min(count, sndbuf_getready(src)); 80766308Scg 808110287Sorion /* When recording only return as much data as available */ 809164614Sariff if (ch->direction == PCMDIR_REC) { 810164614Sariff sndbuf_dispose(src, buffer, l); 811110287Sorion return l; 812164614Sariff } 813110287Sorion 81474763Scg 815164614Sariff offset = count - l; 81674763Scg 817164614Sariff if (offset > 0) { 818164614Sariff if (snd_verbose > 3) 819164614Sariff printf("%s: (%s) %spending %d bytes " 820164614Sariff "(count=%d l=%d feed=%d)\n", 821164614Sariff __func__, 822164614Sariff (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", 823164614Sariff (ch->feedcount == 1) ? "pre" : "ap", 824164614Sariff offset, count, l, ch->feedcount); 825164614Sariff 826164614Sariff if (ch->feedcount == 1) { 827164614Sariff if (offset > 0) 828164614Sariff memset(buffer, 829164614Sariff sndbuf_zerodata(sndbuf_getfmt(src)), 830164614Sariff offset); 831164614Sariff if (l > 0) 832164614Sariff sndbuf_dispose(src, buffer + offset, l); 833164614Sariff else 834164614Sariff ch->feedcount--; 835164614Sariff } else { 836164614Sariff if (l > 0) 837164614Sariff sndbuf_dispose(src, buffer, l); 838164614Sariff if (offset > 0) { 839164614Sariff#if 1 840164614Sariff memset(buffer + l, 841164614Sariff sndbuf_zerodata(sndbuf_getfmt(src)), 842164614Sariff offset); 843164614Sariff if (!(ch->flags & CHN_F_CLOSING)) 844164614Sariff ch->xruns++; 845164614Sariff#else 846164614Sariff if (l < 1 || (ch->flags & CHN_F_CLOSING)) { 847164614Sariff memset(buffer + l, 848164614Sariff sndbuf_zerodata(sndbuf_getfmt(src)), 849164614Sariff offset); 850164614Sariff if (!(ch->flags & CHN_F_CLOSING)) 851164614Sariff ch->xruns++; 852164614Sariff } else { 853164614Sariff int cp, tgt; 854164614Sariff 855164614Sariff tgt = l; 856164614Sariff while (offset > 0) { 857164614Sariff cp = min(l, offset); 858164614Sariff memcpy(buffer + tgt, buffer, cp); 859164614Sariff offset -= cp; 860164614Sariff tgt += cp; 861164614Sariff } 862164614Sariff ch->xruns++; 863164614Sariff } 864164614Sariff#endif 865164614Sariff } 866164614Sariff } 867164614Sariff } else if (l > 0) 868164614Sariff sndbuf_dispose(src, buffer, l); 869164614Sariff 87066308Scg return count; 87166308Scg} 87270134Scg 87370134Scgstatic kobj_method_t feeder_root_methods[] = { 87470134Scg KOBJMETHOD(feeder_feed, feed_root), 87570134Scg { 0, 0 } 87666308Scg}; 87770134Scgstatic struct feeder_class feeder_root_class = { 878118473Sdds .name = "feeder_root", 879118473Sdds .methods = feeder_root_methods, 880118473Sdds .size = sizeof(struct pcm_feeder), 881118473Sdds .align = 0, 882118473Sdds .desc = NULL, 883118473Sdds .data = NULL, 88470134Scg}; 88570134ScgSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 88674363ScgSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 887