feeder.c revision 149950
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 149950 2005-09-10 17:47:39Z netchild $"); 3282180Scg 3370134ScgMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 3470134Scg 3566308Scg#define MAXFEEDERS 256 3665645Scg#undef FEEDER_DEBUG 3750724Scg 3864881Scgstruct feedertab_entry { 3964881Scg SLIST_ENTRY(feedertab_entry) link; 4070134Scg struct feeder_class *feederclass; 4164881Scg struct pcm_feederdesc *desc; 4264881Scg 4364881Scg int idx; 4464881Scg}; 4564881Scgstatic SLIST_HEAD(, feedertab_entry) feedertab; 4664881Scg 4750724Scg/*****************************************************************************/ 4850724Scg 4964881Scgvoid 5064881Scgfeeder_register(void *p) 5164881Scg{ 5289834Scg static int feedercnt = 0; 5389834Scg 5470134Scg struct feeder_class *fc = p; 5564881Scg struct feedertab_entry *fte; 5664881Scg int i; 5764881Scg 5864881Scg if (feedercnt == 0) { 5989834Scg KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 6089834Scg 6164881Scg SLIST_INIT(&feedertab); 62111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 6389834Scg if (fte == NULL) { 6497274Sbde printf("can't allocate memory for root feeder: %s\n", 6597274Sbde fc->name); 6689834Scg 6789834Scg return; 6889834Scg } 6970134Scg fte->feederclass = fc; 7064881Scg fte->desc = NULL; 7164881Scg fte->idx = feedercnt; 7264881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 7364881Scg feedercnt++; 7489834Scg 7589834Scg /* we've got our root feeder so don't veto pcm loading anymore */ 7689834Scg pcm_veto_load = 0; 7789834Scg 7864881Scg return; 7964881Scg } 8064881Scg 8189834Scg KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 8289834Scg 8389834Scg /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 8464881Scg i = 0; 8570134Scg while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 8675355Scg /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 87111909Sorion fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 8889834Scg if (fte == NULL) { 8989834Scg printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 9089834Scg 9189834Scg return; 9289834Scg } 9370134Scg fte->feederclass = fc; 9470134Scg fte->desc = &fc->desc[i]; 9564881Scg fte->idx = feedercnt; 9664881Scg fte->desc->idx = feedercnt; 9764881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 9864881Scg i++; 9964881Scg } 10064881Scg feedercnt++; 10164881Scg if (feedercnt >= MAXFEEDERS) 10289834Scg printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 10364881Scg} 10464881Scg 10574363Scgstatic void 10674363Scgfeeder_unregisterall(void *p) 10774363Scg{ 10874363Scg struct feedertab_entry *fte, *next; 10974363Scg 11074363Scg next = SLIST_FIRST(&feedertab); 11174363Scg while (next != NULL) { 11274363Scg fte = next; 11374363Scg next = SLIST_NEXT(fte, link); 11474363Scg free(fte, M_FEEDER); 11574363Scg } 11674363Scg} 11774363Scg 11850724Scgstatic int 11964881Scgcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 12050724Scg{ 12166308Scg return ((n->type == m->type) && 12266308Scg ((n->in == 0) || (n->in == m->in)) && 12366308Scg ((n->out == 0) || (n->out == m->out)) && 12466308Scg (n->flags == m->flags)); 12550724Scg} 12650724Scg 12770134Scgstatic void 12874763Scgfeeder_destroy(struct pcm_feeder *f) 12950724Scg{ 13070134Scg FEEDER_FREE(f); 13170134Scg kobj_delete((kobj_t)f, M_FEEDER); 13270134Scg} 13364881Scg 13474763Scgstatic struct pcm_feeder * 13570134Scgfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 13670134Scg{ 13774763Scg struct pcm_feeder *f; 13870134Scg int err; 13970134Scg 140111909Sorion f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); 14189834Scg if (f == NULL) 14289834Scg return NULL; 14389834Scg 14470134Scg f->align = fc->align; 14589834Scg f->data = fc->data; 14689834Scg f->source = NULL; 14789834Scg f->parent = NULL; 14889834Scg f->class = fc; 14989834Scg f->desc = &(f->desc_static); 15089834Scg 15189834Scg if (desc) { 15270134Scg *(f->desc) = *desc; 15389834Scg } else { 15470134Scg f->desc->type = FEEDER_ROOT; 15570134Scg f->desc->in = 0; 15670134Scg f->desc->out = 0; 15770134Scg f->desc->flags = 0; 15870134Scg f->desc->idx = 0; 15964881Scg } 16089834Scg 16170134Scg err = FEEDER_INIT(f); 16270134Scg if (err) { 16375319Scg printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 16470134Scg feeder_destroy(f); 16589834Scg 16670134Scg return NULL; 16789834Scg } 16889834Scg 16989834Scg return f; 17050724Scg} 17150724Scg 17270134Scgstruct feeder_class * 17370134Scgfeeder_getclass(struct pcm_feederdesc *desc) 17450724Scg{ 17564881Scg struct feedertab_entry *fte; 17650724Scg 17764881Scg SLIST_FOREACH(fte, &feedertab, link) { 17870134Scg if ((desc == NULL) && (fte->desc == NULL)) 17970134Scg return fte->feederclass; 18070134Scg if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 18170134Scg return fte->feederclass; 18264881Scg } 18364881Scg return NULL; 18450724Scg} 18550724Scg 18650724Scgint 18774763Scgchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 18866308Scg{ 18974763Scg struct pcm_feeder *nf; 19070134Scg 19170134Scg nf = feeder_create(fc, desc); 19270134Scg if (nf == NULL) 19389834Scg return ENOSPC; 19466308Scg 19570134Scg nf->source = c->feeder; 19666308Scg 197117307Scg /* XXX we should use the lowest common denominator for align */ 19866308Scg if (nf->align > 0) 19966308Scg c->align += nf->align; 20066308Scg else if (nf->align < 0 && c->align < -nf->align) 20166308Scg c->align = -nf->align; 202117307Scg if (c->feeder != NULL) 203117307Scg c->feeder->parent = nf; 20466308Scg c->feeder = nf; 20566308Scg 20666308Scg return 0; 20766308Scg} 20866308Scg 20966308Scgint 21074763Scgchn_removefeeder(struct pcm_channel *c) 21150724Scg{ 21274763Scg struct pcm_feeder *f; 21350724Scg 21470134Scg if (c->feeder == NULL) 21564881Scg return -1; 21670134Scg f = c->feeder; 21770134Scg c->feeder = c->feeder->source; 21870134Scg feeder_destroy(f); 21989834Scg 22064881Scg return 0; 22150724Scg} 22250724Scg 22374763Scgstruct pcm_feeder * 22474763Scgchn_findfeeder(struct pcm_channel *c, u_int32_t type) 22566308Scg{ 22674763Scg struct pcm_feeder *f; 22766308Scg 22866308Scg f = c->feeder; 22966308Scg while (f != NULL) { 23066308Scg if (f->desc->type == type) 23166308Scg return f; 23266308Scg f = f->source; 23366308Scg } 23489834Scg 23566308Scg return NULL; 23666308Scg} 23766308Scg 23850724Scgstatic int 23974763Scgchainok(struct pcm_feeder *test, struct pcm_feeder *stop) 24050724Scg{ 24164881Scg u_int32_t visited[MAXFEEDERS / 32]; 24264881Scg u_int32_t idx, mask; 24364881Scg 24464881Scg bzero(visited, sizeof(visited)); 24564881Scg while (test && (test != stop)) { 24664881Scg idx = test->desc->idx; 24764881Scg if (idx < 0) 24864881Scg panic("bad idx %d", idx); 24964881Scg if (idx >= MAXFEEDERS) 25064881Scg panic("bad idx %d", idx); 25164881Scg mask = 1 << (idx & 31); 25264881Scg idx >>= 5; 25364881Scg if (visited[idx] & mask) 25464881Scg return 0; 25564881Scg visited[idx] |= mask; 25664881Scg test = test->source; 25764881Scg } 25889834Scg 25964881Scg return 1; 26050724Scg} 26150724Scg 26274763Scgstatic struct pcm_feeder * 26374763Scgfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 26450724Scg{ 26564881Scg struct feedertab_entry *fte; 26674763Scg struct pcm_feeder *try, *ret; 26764881Scg 26875319Scg /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ 26964881Scg if (fmtvalid(source->desc->out, to)) { 27064881Scg /* printf("got it\n"); */ 27164881Scg return source; 27264881Scg } 27364881Scg 27464881Scg if (maxdepth < 0) 27564881Scg return NULL; 27664881Scg 27764881Scg SLIST_FOREACH(fte, &feedertab, link) { 27875319Scg if (fte->desc == NULL) 27996928Speter continue; 28075319Scg if (fte->desc->type != FEEDER_FMT) 28196928Speter continue; 28275319Scg if (fte->desc->in == source->desc->out) { 28375319Scg try = feeder_create(fte->feederclass, fte->desc); 28475319Scg if (try) { 28575319Scg try->source = source; 28675319Scg ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 28775319Scg if (ret != NULL) 28875319Scg return ret; 28975319Scg feeder_destroy(try); 29075319Scg } 29164881Scg } 29264881Scg } 29375319Scg /* printf("giving up %s...\n", source->class->name); */ 29489834Scg 29564881Scg return NULL; 29650724Scg} 29750724Scg 29864881Scgu_int32_t 29974763Scgchn_fmtchain(struct pcm_channel *c, u_int32_t *to) 30064881Scg{ 30189686Scg struct pcm_feeder *try, *del, *stop; 30289686Scg u_int32_t tmpfrom[2], best, *from; 30389686Scg int i, max, bestmax; 30464881Scg 30589686Scg KASSERT(c != NULL, ("c == NULL")); 30689686Scg KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 30789686Scg KASSERT(to != NULL, ("to == NULL")); 30889686Scg KASSERT(to[0] != 0, ("to[0] == 0")); 30989686Scg 31064881Scg stop = c->feeder; 31189686Scg 31289686Scg if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 31389686Scg from = chn_getcaps(c)->fmtlist; 31489686Scg } else { 31589686Scg tmpfrom[0] = c->feeder->desc->out; 31689686Scg tmpfrom[1] = 0; 31789686Scg from = tmpfrom; 31865645Scg } 31989686Scg 32089686Scg i = 0; 32189686Scg best = 0; 32289686Scg bestmax = 100; 323149950Snetchild while (from[i] != 0) 324149950Snetchild i++; 325149950Snetchild while (i > 0) { 326149950Snetchild i--; 32789686Scg c->feeder->desc->out = from[i]; 32889686Scg try = NULL; 32989686Scg max = 0; 33089686Scg while (try == NULL && max < 8) { 33189686Scg try = feeder_fmtchain(to, c->feeder, stop, max); 33289686Scg if (try == NULL) 33389686Scg max++; 33489686Scg } 33589686Scg if (try != NULL && max < bestmax) { 33689686Scg bestmax = max; 33789686Scg best = from[i]; 33889686Scg } 33989686Scg while (try != NULL && try != stop) { 34089686Scg del = try; 34189686Scg try = try->source; 34289686Scg feeder_destroy(del); 34389686Scg } 34489686Scg } 34589686Scg if (best == 0) 34689686Scg return 0; 34789686Scg 34889686Scg c->feeder->desc->out = best; 34989686Scg try = feeder_fmtchain(to, c->feeder, stop, bestmax); 35064881Scg if (try == NULL) 35164881Scg return 0; 35289686Scg 35364881Scg c->feeder = try; 35464881Scg c->align = 0; 35565645Scg#ifdef FEEDER_DEBUG 35689686Scg printf("\n\nchain: "); 35765645Scg#endif 35864881Scg while (try && (try != stop)) { 35965645Scg#ifdef FEEDER_DEBUG 36075319Scg printf("%s [%d]", try->class->name, try->desc->idx); 36165645Scg if (try->source) 36265645Scg printf(" -> "); 36365645Scg#endif 36489686Scg if (try->source) 36589686Scg try->source->parent = try; 36664881Scg if (try->align > 0) 36764881Scg c->align += try->align; 36864881Scg else if (try->align < 0 && c->align < -try->align) 36964881Scg c->align = -try->align; 37064881Scg try = try->source; 37164881Scg } 37265645Scg#ifdef FEEDER_DEBUG 37375319Scg printf("%s [%d]\n", try->class->name, try->desc->idx); 37465645Scg#endif 37589834Scg 376149950Snetchild if (c->direction == PCMDIR_REC) { 377149950Snetchild try = c->feeder; 378149950Snetchild while (try != NULL) { 379149950Snetchild if (try->desc->type == FEEDER_ROOT) 380149950Snetchild return try->desc->out; 381149950Snetchild try = try->source; 382149950Snetchild } 383149950Snetchild return best; 384149950Snetchild } else 385149950Snetchild return c->feeder->desc->out; 38664881Scg} 38766308Scg 388117307Scgvoid 389117307Scgfeeder_printchain(struct pcm_feeder *head) 390117307Scg{ 391117307Scg struct pcm_feeder *f; 392117307Scg 393117307Scg printf("feeder chain (head @%p)\n", head); 394117307Scg f = head; 395117307Scg while (f != NULL) { 396117307Scg printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); 397117307Scg f = f->source; 398117307Scg } 399117307Scg printf("[end]\n\n"); 400117307Scg} 401117307Scg 40266308Scg/*****************************************************************************/ 40366308Scg 40466308Scgstatic int 40574763Scgfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 40666308Scg{ 40774763Scg struct snd_dbuf *src = source; 40874763Scg int l; 40974763Scg u_int8_t x; 41066308Scg 41174763Scg KASSERT(count > 0, ("feed_root: count == 0")); 41274763Scg /* count &= ~((1 << ch->align) - 1); */ 41374763Scg KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 41466308Scg 41574763Scg l = min(count, sndbuf_getready(src)); 41674763Scg sndbuf_dispose(src, buffer, l); 41766308Scg 418110287Sorion /* When recording only return as much data as available */ 419110287Sorion if (ch->direction == PCMDIR_REC) 420110287Sorion return l; 421110287Sorion 42274763Scg/* 42374763Scg if (l < count) 42474763Scg printf("appending %d bytes\n", count - l); 42574763Scg*/ 42674763Scg 42774763Scg x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80; 42874763Scg while (l < count) 42974763Scg buffer[l++] = x; 43074763Scg 43166308Scg return count; 43266308Scg} 43370134Scg 43470134Scgstatic kobj_method_t feeder_root_methods[] = { 43570134Scg KOBJMETHOD(feeder_feed, feed_root), 43670134Scg { 0, 0 } 43766308Scg}; 43870134Scgstatic struct feeder_class feeder_root_class = { 439118473Sdds .name = "feeder_root", 440118473Sdds .methods = feeder_root_methods, 441118473Sdds .size = sizeof(struct pcm_feeder), 442118473Sdds .align = 0, 443118473Sdds .desc = NULL, 444118473Sdds .data = NULL, 44570134Scg}; 44670134ScgSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 44774363ScgSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 44866308Scg 44966308Scg 45066308Scg 45166308Scg 45270134Scg 453