feeder.c revision 170884
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 170884 2007-06-17 15:53:11Z ariff $"); 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); 40167644Sariff 41167644Sariff#ifdef SND_DEBUG 42167644Sariffstatic int 43167644Sariffsysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS) 44167644Sariff{ 45167644Sariff int i, err, val; 46167644Sariff 47167644Sariff val = feeder_buffersize; 48170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 49167644Sariff 50167644Sariff if (err != 0 || req->newptr == NULL) 51167644Sariff return err; 52167644Sariff 53167644Sariff if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX) 54167644Sariff return EINVAL; 55167644Sariff 56167644Sariff i = 0; 57167644Sariff while (val >> i) 58167644Sariff i++; 59167644Sariff i = 1 << i; 60167644Sariff if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2)) 61167644Sariff i >>= 1; 62167644Sariff 63167644Sariff feeder_buffersize = i; 64167644Sariff 65167644Sariff return err; 66167644Sariff} 67167644SariffSYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW, 68167644Sariff 0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I", 69167644Sariff "feeder buffer size"); 70167644Sariff#else 71164614SariffSYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, 72164614Sariff &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); 73167644Sariff#endif 74164614Sariff 7564881Scgstruct feedertab_entry { 7664881Scg SLIST_ENTRY(feedertab_entry) link; 7770134Scg struct feeder_class *feederclass; 7864881Scg struct pcm_feederdesc *desc; 7964881Scg 8064881Scg int idx; 8164881Scg}; 8264881Scgstatic SLIST_HEAD(, feedertab_entry) feedertab; 8364881Scg 8450724Scg/*****************************************************************************/ 8550724Scg 8664881Scgvoid 8764881Scgfeeder_register(void *p) 8864881Scg{ 8989834Scg static int feedercnt = 0; 9089834Scg 9170134Scg struct feeder_class *fc = p; 9264881Scg struct feedertab_entry *fte; 9364881Scg int i; 9464881Scg 9564881Scg if (feedercnt == 0) { 9689834Scg KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 9789834Scg 9864881Scg SLIST_INIT(&feedertab); 99111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 10089834Scg if (fte == NULL) { 10197274Sbde printf("can't allocate memory for root feeder: %s\n", 10297274Sbde fc->name); 10389834Scg 10489834Scg return; 10589834Scg } 10670134Scg fte->feederclass = fc; 10764881Scg fte->desc = NULL; 10864881Scg fte->idx = feedercnt; 10964881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 11064881Scg feedercnt++; 11189834Scg 112164614Sariff /* initialize global variables */ 113164614Sariff 114170161Sariff if (snd_verbose < 0 || snd_verbose > 4) 115164614Sariff snd_verbose = 1; 116164614Sariff 117170161Sariff /* initialize unit numbering */ 118170161Sariff snd_unit_init(); 119170161Sariff if (snd_unit < 0 || snd_unit > PCMMAXUNIT) 120170884Sariff snd_unit = -1; 121164614Sariff 122164614Sariff if (snd_maxautovchans < 0 || 123164614Sariff snd_maxautovchans > SND_MAXVCHANS) 124164614Sariff snd_maxautovchans = 0; 125164614Sariff 126164614Sariff if (chn_latency < CHN_LATENCY_MIN || 127164614Sariff chn_latency > CHN_LATENCY_MAX) 128164614Sariff chn_latency = CHN_LATENCY_DEFAULT; 129164614Sariff 130164614Sariff if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || 131164614Sariff chn_latency_profile > CHN_LATENCY_PROFILE_MAX) 132164614Sariff chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 133164614Sariff 134164614Sariff if (feeder_buffersize < FEEDBUFSZ_MIN || 135164614Sariff feeder_buffersize > FEEDBUFSZ_MAX) 136164614Sariff feeder_buffersize = FEEDBUFSZ; 137164614Sariff 138164614Sariff if (feeder_rate_min < FEEDRATE_MIN || 139164614Sariff feeder_rate_max < FEEDRATE_MIN || 140164614Sariff feeder_rate_min > FEEDRATE_MAX || 141164614Sariff feeder_rate_max > FEEDRATE_MAX || 142164614Sariff !(feeder_rate_min < feeder_rate_max)) { 143164614Sariff feeder_rate_min = FEEDRATE_RATEMIN; 144164614Sariff feeder_rate_max = FEEDRATE_RATEMAX; 145164614Sariff } 146164614Sariff 147164614Sariff if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || 148164614Sariff feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) 149164614Sariff feeder_rate_round = FEEDRATE_ROUNDHZ; 150164614Sariff 151164614Sariff if (bootverbose) 152164614Sariff printf("%s: snd_unit=%d snd_maxautovchans=%d " 153164614Sariff "latency=%d feeder_buffersize=%d " 154164614Sariff "feeder_rate_min=%d feeder_rate_max=%d " 155164614Sariff "feeder_rate_round=%d\n", 156164614Sariff __func__, snd_unit, snd_maxautovchans, 157164614Sariff chn_latency, feeder_buffersize, 158164614Sariff feeder_rate_min, feeder_rate_max, 159164614Sariff feeder_rate_round); 160164614Sariff 16189834Scg /* we've got our root feeder so don't veto pcm loading anymore */ 16289834Scg pcm_veto_load = 0; 16389834Scg 16464881Scg return; 16564881Scg } 16664881Scg 16789834Scg KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 16889834Scg 16989834Scg /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 17064881Scg i = 0; 17170134Scg while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 17275355Scg /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 173111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 17489834Scg if (fte == NULL) { 17589834Scg printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 17689834Scg 17789834Scg return; 17889834Scg } 17970134Scg fte->feederclass = fc; 18070134Scg fte->desc = &fc->desc[i]; 18164881Scg fte->idx = feedercnt; 18264881Scg fte->desc->idx = feedercnt; 18364881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 18464881Scg i++; 18564881Scg } 18664881Scg feedercnt++; 18764881Scg if (feedercnt >= MAXFEEDERS) 18889834Scg printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 18964881Scg} 19064881Scg 19174363Scgstatic void 19274363Scgfeeder_unregisterall(void *p) 19374363Scg{ 19474363Scg struct feedertab_entry *fte, *next; 19574363Scg 19674363Scg next = SLIST_FIRST(&feedertab); 19774363Scg while (next != NULL) { 19874363Scg fte = next; 19974363Scg next = SLIST_NEXT(fte, link); 20074363Scg free(fte, M_FEEDER); 20174363Scg } 20274363Scg} 20374363Scg 20450724Scgstatic int 20564881Scgcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 20650724Scg{ 20766308Scg return ((n->type == m->type) && 20866308Scg ((n->in == 0) || (n->in == m->in)) && 20966308Scg ((n->out == 0) || (n->out == m->out)) && 21066308Scg (n->flags == m->flags)); 21150724Scg} 21250724Scg 21370134Scgstatic void 21474763Scgfeeder_destroy(struct pcm_feeder *f) 21550724Scg{ 21670134Scg FEEDER_FREE(f); 21770134Scg kobj_delete((kobj_t)f, M_FEEDER); 21870134Scg} 21964881Scg 22074763Scgstatic struct pcm_feeder * 22170134Scgfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 22270134Scg{ 22374763Scg struct pcm_feeder *f; 22470134Scg int err; 22570134Scg 226111909Sorion f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); 22789834Scg if (f == NULL) 22889834Scg return NULL; 22989834Scg 23070134Scg f->align = fc->align; 23189834Scg f->data = fc->data; 23289834Scg f->source = NULL; 23389834Scg f->parent = NULL; 23489834Scg f->class = fc; 23589834Scg f->desc = &(f->desc_static); 23689834Scg 23789834Scg if (desc) { 23870134Scg *(f->desc) = *desc; 23989834Scg } else { 24070134Scg f->desc->type = FEEDER_ROOT; 24170134Scg f->desc->in = 0; 24270134Scg f->desc->out = 0; 24370134Scg f->desc->flags = 0; 24470134Scg f->desc->idx = 0; 24564881Scg } 24689834Scg 24770134Scg err = FEEDER_INIT(f); 24870134Scg if (err) { 24975319Scg printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 25070134Scg feeder_destroy(f); 25189834Scg 25270134Scg return NULL; 25389834Scg } 25489834Scg 25589834Scg return f; 25650724Scg} 25750724Scg 25870134Scgstruct feeder_class * 25970134Scgfeeder_getclass(struct pcm_feederdesc *desc) 26050724Scg{ 26164881Scg struct feedertab_entry *fte; 26250724Scg 26364881Scg SLIST_FOREACH(fte, &feedertab, link) { 26470134Scg if ((desc == NULL) && (fte->desc == NULL)) 26570134Scg return fte->feederclass; 26670134Scg if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 26770134Scg return fte->feederclass; 26864881Scg } 26964881Scg return NULL; 27050724Scg} 27150724Scg 27250724Scgint 27374763Scgchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 27466308Scg{ 27574763Scg struct pcm_feeder *nf; 27670134Scg 27770134Scg nf = feeder_create(fc, desc); 27870134Scg if (nf == NULL) 27989834Scg return ENOSPC; 28066308Scg 28170134Scg nf->source = c->feeder; 28266308Scg 283117307Scg /* XXX we should use the lowest common denominator for align */ 28466308Scg if (nf->align > 0) 28566308Scg c->align += nf->align; 28666308Scg else if (nf->align < 0 && c->align < -nf->align) 28766308Scg c->align = -nf->align; 288117307Scg if (c->feeder != NULL) 289117307Scg c->feeder->parent = nf; 29066308Scg c->feeder = nf; 29166308Scg 29266308Scg return 0; 29366308Scg} 29466308Scg 29566308Scgint 29674763Scgchn_removefeeder(struct pcm_channel *c) 29750724Scg{ 29874763Scg struct pcm_feeder *f; 29950724Scg 30070134Scg if (c->feeder == NULL) 30164881Scg return -1; 30270134Scg f = c->feeder; 30370134Scg c->feeder = c->feeder->source; 30470134Scg feeder_destroy(f); 30589834Scg 30664881Scg return 0; 30750724Scg} 30850724Scg 30974763Scgstruct pcm_feeder * 31074763Scgchn_findfeeder(struct pcm_channel *c, u_int32_t type) 31166308Scg{ 31274763Scg struct pcm_feeder *f; 31366308Scg 31466308Scg f = c->feeder; 31566308Scg while (f != NULL) { 31666308Scg if (f->desc->type == type) 31766308Scg return f; 31866308Scg f = f->source; 31966308Scg } 32089834Scg 32166308Scg return NULL; 32266308Scg} 32366308Scg 32450724Scgstatic int 32574763Scgchainok(struct pcm_feeder *test, struct pcm_feeder *stop) 32650724Scg{ 32764881Scg u_int32_t visited[MAXFEEDERS / 32]; 32864881Scg u_int32_t idx, mask; 32964881Scg 33064881Scg bzero(visited, sizeof(visited)); 33164881Scg while (test && (test != stop)) { 33264881Scg idx = test->desc->idx; 33364881Scg if (idx < 0) 33464881Scg panic("bad idx %d", idx); 33564881Scg if (idx >= MAXFEEDERS) 33664881Scg panic("bad idx %d", idx); 33764881Scg mask = 1 << (idx & 31); 33864881Scg idx >>= 5; 33964881Scg if (visited[idx] & mask) 34064881Scg return 0; 34164881Scg visited[idx] |= mask; 34264881Scg test = test->source; 34364881Scg } 34489834Scg 34564881Scg return 1; 34650724Scg} 34750724Scg 348164614Sariff/* 349170161Sariff * See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation 350164614Sariff * of what the heck is this FMT_Q_* 351164614Sariff */ 352164614Sariff#define FMT_Q_UP 1 353164614Sariff#define FMT_Q_DOWN 2 354164614Sariff#define FMT_Q_EQ 3 355164614Sariff#define FMT_Q_MULTI 4 35664881Scg 357164614Sariff/* 358164614Sariff * 14bit format scoring 359164614Sariff * -------------------- 360164614Sariff * 361164614Sariff * 13 12 11 10 9 8 2 1 0 offset 362164614Sariff * +---+---+---+---+---+---+-------------+---+---+ 363164614Sariff * | X | X | X | X | X | X | X X X X X X | X | X | 364164614Sariff * +---+---+---+---+---+---+-------------+---+---+ 365164614Sariff * | | | | | | | | | 366164614Sariff * | | | | | | | | +--> signed? 367164614Sariff * | | | | | | | | 368164614Sariff * | | | | | | | +------> bigendian? 369164614Sariff * | | | | | | | 370164614Sariff * | | | | | | +---------------> total channels 371164614Sariff * | | | | | | 372164614Sariff * | | | | | +------------------------> AFMT_A_LAW 373164614Sariff * | | | | | 374164614Sariff * | | | | +----------------------------> AFMT_MU_LAW 375164614Sariff * | | | | 376164614Sariff * | | | +--------------------------------> AFMT_8BIT 377164614Sariff * | | | 378164614Sariff * | | +------------------------------------> AFMT_16BIT 379164614Sariff * | | 380164614Sariff * | +----------------------------------------> AFMT_24BIT 381164614Sariff * | 382164614Sariff * +--------------------------------------------> AFMT_32BIT 383164614Sariff */ 384164614Sariff#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) 385164614Sariff#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) 386164614Sariff#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) 387164614Sariff#define score_val(s1) ((s1) & 0x3f00) 388164614Sariff#define score_cse(s1) ((s1) & 0x7f) 38964881Scg 390164614Sariffu_int32_t 391154684Sariffchn_fmtscore(u_int32_t fmt) 392154684Sariff{ 393164614Sariff u_int32_t ret; 394164614Sariff 395164614Sariff ret = 0; 396164614Sariff if (fmt & AFMT_SIGNED) 397164614Sariff ret |= 1 << 0; 398164614Sariff if (fmt & AFMT_BIGENDIAN) 399164614Sariff ret |= 1 << 1; 400164614Sariff if (fmt & AFMT_STEREO) 401164614Sariff ret |= (2 & 0x3f) << 2; 402164614Sariff else 403164614Sariff ret |= (1 & 0x3f) << 2; 404154969Sariff if (fmt & AFMT_A_LAW) 405164614Sariff ret |= 1 << 8; 406164614Sariff else if (fmt & AFMT_MU_LAW) 407164614Sariff ret |= 1 << 9; 408164614Sariff else if (fmt & AFMT_8BIT) 409164614Sariff ret |= 1 << 10; 410164614Sariff else if (fmt & AFMT_16BIT) 411164614Sariff ret |= 1 << 11; 412164614Sariff else if (fmt & AFMT_24BIT) 413164614Sariff ret |= 1 << 12; 414164614Sariff else if (fmt & AFMT_32BIT) 415164614Sariff ret |= 1 << 13; 416164614Sariff 417164614Sariff return ret; 418154684Sariff} 419154684Sariff 420164614Sariffstatic u_int32_t 421164614Sariffchn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) 422154684Sariff{ 423164614Sariff u_int32_t best, score, score2, oldscore; 424164614Sariff int i; 425154684Sariff 426164614Sariff if (fmt == 0 || fmts == NULL || fmts[0] == 0) 427164614Sariff return 0; 428164614Sariff 429164614Sariff if (fmtvalid(fmt, fmts)) 430164614Sariff return fmt; 431164614Sariff 432154684Sariff best = 0; 433154684Sariff score = chn_fmtscore(fmt); 434154684Sariff oldscore = 0; 435154684Sariff for (i = 0; fmts[i] != 0; i++) { 436154684Sariff score2 = chn_fmtscore(fmts[i]); 437164614Sariff if (cheq && !score_cheq(score, score2)) 438164614Sariff continue; 439164614Sariff if (oldscore == 0 || 440164614Sariff (score_val(score2) == score_val(score)) || 441164614Sariff (score_val(score2) == score_val(oldscore)) || 442164614Sariff (score_val(score2) > score_val(oldscore) && 443164614Sariff score_val(score2) < score_val(score)) || 444164614Sariff (score_val(score2) < score_val(oldscore) && 445164614Sariff score_val(score2) > score_val(score)) || 446164614Sariff (score_val(oldscore) < score_val(score) && 447164614Sariff score_val(score2) > score_val(oldscore))) { 448164614Sariff if (score_val(oldscore) != score_val(score2) || 449164614Sariff score_cse(score) == score_cse(score2) || 450164614Sariff ((score_cse(oldscore) != score_cse(score) && 451164614Sariff !score_endianeq(score, oldscore) && 452164614Sariff (score_endianeq(score, score2) || 453164614Sariff (!score_signeq(score, oldscore) && 454164614Sariff score_signeq(score, score2)))))) { 455164614Sariff best = fmts[i]; 456164614Sariff oldscore = score2; 457164614Sariff } 458154684Sariff } 459154684Sariff } 460154684Sariff return best; 461154684Sariff} 462154684Sariff 463154684Sariffu_int32_t 464164614Sariffchn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) 465164614Sariff{ 466164614Sariff return chn_fmtbestfunc(fmt, fmts, 0); 467164614Sariff} 468164614Sariff 469164614Sariffu_int32_t 470154684Sariffchn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) 471154684Sariff{ 472164614Sariff return chn_fmtbestfunc(fmt, fmts, 1); 473154684Sariff} 474154684Sariff 475154684Sariffu_int32_t 476154684Sariffchn_fmtbest(u_int32_t fmt, u_int32_t *fmts) 477154684Sariff{ 478154684Sariff u_int32_t best1, best2; 479164614Sariff u_int32_t score, score1, score2; 480154684Sariff 481164614Sariff if (fmtvalid(fmt, fmts)) 482164614Sariff return fmt; 483164614Sariff 484154684Sariff best1 = chn_fmtbeststereo(fmt, fmts); 485154684Sariff best2 = chn_fmtbestbit(fmt, fmts); 486154684Sariff 487164614Sariff if (best1 != 0 && best2 != 0 && best1 != best2) { 488154684Sariff if (fmt & AFMT_STEREO) 489154684Sariff return best1; 490154684Sariff else { 491164614Sariff score = score_val(chn_fmtscore(fmt)); 492164614Sariff score1 = score_val(chn_fmtscore(best1)); 493164614Sariff score2 = score_val(chn_fmtscore(best2)); 494154969Sariff if (score1 == score2 || score1 == score) 495154969Sariff return best1; 496154969Sariff else if (score2 == score) 497154684Sariff return best2; 498154969Sariff else if (score1 > score2) 499154684Sariff return best1; 500154684Sariff return best2; 501154684Sariff } 502154684Sariff } else if (best2 == 0) 503154684Sariff return best1; 504155958Sjhb else 505154684Sariff return best2; 506154684Sariff} 507154684Sariff 508164614Sariffstatic struct pcm_feeder * 509164614Sarifffeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 510164614Sariff{ 511164614Sariff struct feedertab_entry *fte, *ftebest; 512164614Sariff struct pcm_feeder *try, *ret; 513164614Sariff uint32_t fl, qout, qsrc, qdst; 514164614Sariff int qtype; 515164614Sariff 516164614Sariff if (to == NULL || to[0] == 0) 517164614Sariff return NULL; 518164614Sariff 519164614Sariff DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); 520164614Sariff if (fmtvalid(source->desc->out, to)) { 521164614Sariff DEB(printf("got it\n")); 522164614Sariff return source; 523164614Sariff } 524164614Sariff 525164614Sariff if (maxdepth < 0) 526164614Sariff return NULL; 527164614Sariff 528164614Sariff /* 529164614Sariff * WARNING: THIS IS _NOT_ FOR THE FAINT HEART 530164614Sariff * Disclaimer: I don't expect anybody could understand this 531164614Sariff * without deep logical and mathematical analysis 532164614Sariff * involving various unnamed probability theorem. 533164614Sariff * 534164614Sariff * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm 535164614Sariff * is **extremely** difficult to digest especially when applied to 536164614Sariff * large sets / numbers of random chains (feeders), each with 537164614Sariff * unique characteristic providing different sets of in/out format. 538164614Sariff * 539164614Sariff * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: 540164614Sariff * 1) Format chains 541164614Sariff * 1.1 "8bit to any, not to 8bit" 542164614Sariff * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] 543164614Sariff * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] 544164614Sariff * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] 545164614Sariff * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] 546164614Sariff * 1.2 "Any to 8bit, not from 8bit" 547164614Sariff * 1.2.1 sign can remain consistent, e.g: s16le -> s8 548164614Sariff * 1.2.2 sign can be changed, e.g: s16le -> u8 549164614Sariff * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 550164614Sariff * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 551164614Sariff * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" 552164614Sariff * 1.3.1 endian MUST remain consistent 553164614Sariff * 1.3.2 sign CAN be changed 554164614Sariff * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding 555164614Sariff * 16bit to 24bit . 556164614Sariff * 2) Channel chains (mono <-> stereo) 557164614Sariff * 2.1 Both endian and sign MUST remain consistent 558164614Sariff * 3) Endian chains (big endian <-> little endian) 559164614Sariff * 3.1 Channels and sign MUST remain consistent 560164614Sariff * 4) Sign chains (signed <-> unsigned) 561164614Sariff * 4.1 Channels and endian MUST remain consistent 562164614Sariff * 563164614Sariff * .. and the mother of all chaining rules: 564164614Sariff * 565164614Sariff * Rules 0: Source and destination MUST not contain multiple selections. 566164614Sariff * (qtype != FMT_Q_MULTI) 567164614Sariff * 568164614Sariff * First of all, our caller ( chn_fmtchain() ) will reduce the possible 569164614Sariff * multiple from/to formats to a single best format using chn_fmtbest(). 570164614Sariff * Then, using chn_fmtscore(), we determine the chaining characteristic. 571164614Sariff * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining 572164614Sariff * type while still adhering above chaining rules. 573164614Sariff * 574164614Sariff * The need for this complicated chaining procedures is inevitable, 575164614Sariff * since currently we have more than 200 different types of FEEDER_FMT 576164614Sariff * doing various unique format conversion. Without this (the old way), 577164614Sariff * it is possible to generate broken chain since it doesn't do any 578164614Sariff * sanity checking to ensure that the output format is "properly aligned" 579164614Sariff * with the direction of conversion (quality up/down/equal). 580164614Sariff * 581164614Sariff * Conversion: s24le to s32le 582164614Sariff * Possible chain: 1) s24le -> s32le (correct, optimized) 583164614Sariff * 2) s24le -> s16le -> s32le 584164614Sariff * (since we have feeder_24to16 and feeder_16to32) 585164614Sariff * +-- obviously broken! 586164614Sariff * 587164614Sariff * Using scoring mechanisme, this will ensure that the chaining 588164614Sariff * process do the right thing, or at least, give the best chain 589164614Sariff * possible without causing quality (the 'Q') degradation. 590164614Sariff */ 591164614Sariff 592164614Sariff qdst = chn_fmtscore(to[0]); 593164614Sariff qsrc = chn_fmtscore(source->desc->out); 594164614Sariff 595164614Sariff#define score_q(s1) score_val(s1) 596164614Sariff#define score_8bit(s1) ((s1) & 0x700) 597164614Sariff#define score_non8bit(s1) (!score_8bit(s1)) 598164614Sariff#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ 599164614Sariff (score_8bit(s2) && score_non8bit(s1))) 600164614Sariff 601164614Sariff#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) 602164614Sariff#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) 603164614Sariff#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) 604164614Sariff#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ 605164614Sariff 0x2 : 0x0)) 606164614Sariff#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) 607164614Sariff#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ 608164614Sariff ((score_cheq(s1, s2) && \ 609164614Sariff score_endianeq(s1, s2)) ? \ 610164614Sariff 0x1 : 0x0) | \ 611164614Sariff ((score_cheq(s1, s2) && \ 612164614Sariff score_signeq(s1, s2)) ? \ 613164614Sariff 0x2 : 0x0)) 614164614Sariff 615164614Sariff /* Determine chaining direction and set matching flag */ 616164614Sariff fl = 0x3fff; 617164614Sariff if (to[1] != 0) { 618164614Sariff qtype = FMT_Q_MULTI; 619164614Sariff printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); 620164614Sariff } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { 621164614Sariff qtype = FMT_Q_DOWN; 622164614Sariff fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); 623164614Sariff } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { 624164614Sariff qtype = FMT_Q_UP; 625164614Sariff fl = FMT_Q_UP_FLAGS(qsrc, qdst); 626164614Sariff } else { 627164614Sariff qtype = FMT_Q_EQ; 628164614Sariff fl = FMT_Q_EQ_FLAGS(qsrc, qdst); 629164614Sariff } 630164614Sariff 631164614Sariff ftebest = NULL; 632164614Sariff 633164614Sariff SLIST_FOREACH(fte, &feedertab, link) { 634164614Sariff if (fte->desc == NULL) 635164614Sariff continue; 636164614Sariff if (fte->desc->type != FEEDER_FMT) 637164614Sariff continue; 638164614Sariff qout = chn_fmtscore(fte->desc->out); 639164614Sariff#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) 640164614Sariff#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) 641164614Sariff#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ 642164614Sariff score_q(s3) >= score_q(s1) && \ 643164614Sariff score_q(s3) <= score_q(s2)) 644164614Sariff#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ 645164614Sariff score_q(s3) <= score_q(s1) && \ 646164614Sariff score_q(s3) >= score_q(s2)) 647164614Sariff#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ 648164614Sariff score_q(s1) == score_q(s2)) 649164614Sariff if (fte->desc->in == source->desc->out && 650164614Sariff (FMT_Q_MULTI_VALIDATE(qtype) || 651164614Sariff (FMT_Q_FL_MATCH(fl, qout, qdst) && 652164614Sariff (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || 653164614Sariff FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || 654164614Sariff FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { 655164614Sariff try = feeder_create(fte->feederclass, fte->desc); 656164614Sariff if (try) { 657164614Sariff try->source = source; 658164614Sariff ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 659164614Sariff if (ret != NULL) 660164614Sariff return ret; 661164614Sariff feeder_destroy(try); 662164614Sariff } 663164614Sariff } else if (fte->desc->in == source->desc->out) { 664164614Sariff /* XXX quality must be considered! */ 665164614Sariff if (ftebest == NULL) 666164614Sariff ftebest = fte; 667164614Sariff } 668164614Sariff } 669164614Sariff 670164614Sariff if (ftebest != NULL) { 671164614Sariff try = feeder_create(ftebest->feederclass, ftebest->desc); 672164614Sariff if (try) { 673164614Sariff try->source = source; 674164614Sariff ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 675164614Sariff if (ret != NULL) 676164614Sariff return ret; 677164614Sariff feeder_destroy(try); 678164614Sariff } 679164614Sariff } 680164614Sariff 681164614Sariff /* printf("giving up %s...\n", source->class->name); */ 682164614Sariff 683164614Sariff return NULL; 684164614Sariff} 685164614Sariff 686154684Sariffu_int32_t 68774763Scgchn_fmtchain(struct pcm_channel *c, u_int32_t *to) 68864881Scg{ 68989686Scg struct pcm_feeder *try, *del, *stop; 690154684Sariff u_int32_t tmpfrom[2], tmpto[2], best, *from; 69189686Scg int i, max, bestmax; 69264881Scg 69389686Scg KASSERT(c != NULL, ("c == NULL")); 69489686Scg KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 69589686Scg KASSERT(to != NULL, ("to == NULL")); 69689686Scg KASSERT(to[0] != 0, ("to[0] == 0")); 69789686Scg 698164614Sariff if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) 699164614Sariff return 0; 700164614Sariff 70164881Scg stop = c->feeder; 702164614Sariff best = 0; 70389686Scg 70489686Scg if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 70589686Scg from = chn_getcaps(c)->fmtlist; 706164614Sariff if (from[1] != 0) { 707154684Sariff best = chn_fmtbest(to[0], from); 708154684Sariff if (best != 0) { 709154684Sariff tmpfrom[0] = best; 710154684Sariff tmpfrom[1] = 0; 711154684Sariff from = tmpfrom; 712154684Sariff } 713154684Sariff } 71489686Scg } else { 71589686Scg tmpfrom[0] = c->feeder->desc->out; 71689686Scg tmpfrom[1] = 0; 71789686Scg from = tmpfrom; 718154684Sariff if (to[1] != 0) { 719164614Sariff best = chn_fmtbest(from[0], to); 720164614Sariff if (best != 0) { 721164614Sariff tmpto[0] = best; 722154684Sariff tmpto[1] = 0; 723154684Sariff to = tmpto; 724154684Sariff } 725154684Sariff } 72665645Scg } 72789686Scg 728164614Sariff#define FEEDER_FMTCHAIN_MAXDEPTH 8 729164614Sariff 730164614Sariff try = NULL; 731164614Sariff 732164614Sariff if (to[0] != 0 && from[0] != 0 && 733164614Sariff to[1] == 0 && from[1] == 0) { 73489686Scg max = 0; 735164614Sariff best = from[0]; 736164614Sariff c->feeder->desc->out = best; 737164614Sariff do { 73889686Scg try = feeder_fmtchain(to, c->feeder, stop, max); 739164614Sariff DEB(if (try != NULL) { 740164614Sariff printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", 741165835Snetchild __func__, from[0], to[0], max); 742164614Sariff }); 743164614Sariff } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); 744164614Sariff } else { 745164614Sariff printf("%s: Using the old-way format chaining!\n", __func__); 746164614Sariff i = 0; 747164614Sariff best = 0; 748164614Sariff bestmax = 100; 749164614Sariff while (from[i] != 0) { 750164614Sariff c->feeder->desc->out = from[i]; 751164614Sariff try = NULL; 752164614Sariff max = 0; 753164614Sariff do { 754164614Sariff try = feeder_fmtchain(to, c->feeder, stop, max); 755164614Sariff } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); 756164614Sariff if (try != NULL && max < bestmax) { 757164614Sariff bestmax = max; 758164614Sariff best = from[i]; 759164614Sariff } 760164614Sariff while (try != NULL && try != stop) { 761164614Sariff del = try; 762164614Sariff try = try->source; 763164614Sariff feeder_destroy(del); 764164614Sariff } 765164614Sariff i++; 76689686Scg } 767164614Sariff if (best == 0) 768164614Sariff return 0; 769164614Sariff 770164614Sariff c->feeder->desc->out = best; 771164614Sariff try = feeder_fmtchain(to, c->feeder, stop, bestmax); 77289686Scg } 77364881Scg if (try == NULL) 77464881Scg return 0; 77589686Scg 77664881Scg c->feeder = try; 77764881Scg c->align = 0; 77865645Scg#ifdef FEEDER_DEBUG 77989686Scg printf("\n\nchain: "); 78065645Scg#endif 78164881Scg while (try && (try != stop)) { 78265645Scg#ifdef FEEDER_DEBUG 78375319Scg printf("%s [%d]", try->class->name, try->desc->idx); 78465645Scg if (try->source) 78565645Scg printf(" -> "); 78665645Scg#endif 78789686Scg if (try->source) 78889686Scg try->source->parent = try; 78964881Scg if (try->align > 0) 79064881Scg c->align += try->align; 79164881Scg else if (try->align < 0 && c->align < -try->align) 79264881Scg c->align = -try->align; 79364881Scg try = try->source; 79464881Scg } 79565645Scg#ifdef FEEDER_DEBUG 79675319Scg printf("%s [%d]\n", try->class->name, try->desc->idx); 79765645Scg#endif 79889834Scg 799149950Snetchild if (c->direction == PCMDIR_REC) { 800149950Snetchild try = c->feeder; 801149950Snetchild while (try != NULL) { 802149950Snetchild if (try->desc->type == FEEDER_ROOT) 803149950Snetchild return try->desc->out; 804149950Snetchild try = try->source; 805149950Snetchild } 806149950Snetchild return best; 807149950Snetchild } else 808149950Snetchild return c->feeder->desc->out; 80964881Scg} 81066308Scg 811117307Scgvoid 812117307Scgfeeder_printchain(struct pcm_feeder *head) 813117307Scg{ 814117307Scg struct pcm_feeder *f; 815117307Scg 816117307Scg printf("feeder chain (head @%p)\n", head); 817117307Scg f = head; 818117307Scg while (f != NULL) { 819117307Scg printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); 820117307Scg f = f->source; 821117307Scg } 822117307Scg printf("[end]\n\n"); 823117307Scg} 824117307Scg 82566308Scg/*****************************************************************************/ 82666308Scg 82766308Scgstatic int 82874763Scgfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 82966308Scg{ 83074763Scg struct snd_dbuf *src = source; 831164614Sariff int l, offset; 83266308Scg 83374763Scg KASSERT(count > 0, ("feed_root: count == 0")); 83474763Scg /* count &= ~((1 << ch->align) - 1); */ 83574763Scg KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 83666308Scg 837164614Sariff if (++ch->feedcount == 0) 838164614Sariff ch->feedcount = 2; 839164614Sariff 84074763Scg l = min(count, sndbuf_getready(src)); 84166308Scg 842110287Sorion /* When recording only return as much data as available */ 843164614Sariff if (ch->direction == PCMDIR_REC) { 844164614Sariff sndbuf_dispose(src, buffer, l); 845110287Sorion return l; 846164614Sariff } 847110287Sorion 84874763Scg 849164614Sariff offset = count - l; 85074763Scg 851164614Sariff if (offset > 0) { 852164614Sariff if (snd_verbose > 3) 853164614Sariff printf("%s: (%s) %spending %d bytes " 854164614Sariff "(count=%d l=%d feed=%d)\n", 855164614Sariff __func__, 856164614Sariff (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", 857164614Sariff (ch->feedcount == 1) ? "pre" : "ap", 858164614Sariff offset, count, l, ch->feedcount); 859164614Sariff 860164614Sariff if (ch->feedcount == 1) { 861167644Sariff memset(buffer, 862167644Sariff sndbuf_zerodata(sndbuf_getfmt(src)), 863167644Sariff offset); 864164614Sariff if (l > 0) 865164614Sariff sndbuf_dispose(src, buffer + offset, l); 866164614Sariff else 867164614Sariff ch->feedcount--; 868164614Sariff } else { 869164614Sariff if (l > 0) 870164614Sariff sndbuf_dispose(src, buffer, l); 871167644Sariff memset(buffer + l, 872167644Sariff sndbuf_zerodata(sndbuf_getfmt(src)), 873167644Sariff offset); 874167644Sariff if (!(ch->flags & CHN_F_CLOSING)) 875167644Sariff ch->xruns++; 876164614Sariff } 877164614Sariff } else if (l > 0) 878164614Sariff sndbuf_dispose(src, buffer, l); 879164614Sariff 88066308Scg return count; 88166308Scg} 88270134Scg 88370134Scgstatic kobj_method_t feeder_root_methods[] = { 88470134Scg KOBJMETHOD(feeder_feed, feed_root), 88570134Scg { 0, 0 } 88666308Scg}; 88770134Scgstatic struct feeder_class feeder_root_class = { 888118473Sdds .name = "feeder_root", 889118473Sdds .methods = feeder_root_methods, 890118473Sdds .size = sizeof(struct pcm_feeder), 891118473Sdds .align = 0, 892118473Sdds .desc = NULL, 893118473Sdds .data = NULL, 89470134Scg}; 89570134ScgSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 89674363ScgSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 897