feeder.c revision 74763
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 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 * 2650733Speter * $FreeBSD: head/sys/dev/sound/pcm/feeder.c 74763 2001-03-24 23:10:29Z cg $ 2750724Scg */ 2850724Scg 2953465Scg#include <dev/sound/pcm/sound.h> 3050724Scg 3170134Scg#include "feeder_if.h" 3270134Scg 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{ 5270134Scg struct feeder_class *fc = p; 5364881Scg struct feedertab_entry *fte; 5464881Scg static int feedercnt = 0; 5564881Scg int i; 5664881Scg 5764881Scg if (feedercnt == 0) { 5870134Scg if (fc->desc) 5970134Scg panic("FIRST FEEDER NOT ROOT: %s\n", fc->name); 6064881Scg SLIST_INIT(&feedertab); 6170134Scg fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 6270134Scg fte->feederclass = fc; 6364881Scg fte->desc = NULL; 6464881Scg fte->idx = feedercnt; 6564881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 6664881Scg feedercnt++; 6764881Scg return; 6864881Scg } 6964881Scg 7064881Scg i = 0; 7170134Scg while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 7270134Scg fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 7370134Scg fte->feederclass = fc; 7470134Scg fte->desc = &fc->desc[i]; 7564881Scg fte->idx = feedercnt; 7664881Scg fte->desc->idx = feedercnt; 7764881Scg SLIST_INSERT_HEAD(&feedertab, fte, link); 7864881Scg i++; 7964881Scg } 8064881Scg feedercnt++; 8164881Scg if (feedercnt >= MAXFEEDERS) 8264881Scg printf("MAXFEEDERS exceeded\n"); 8364881Scg} 8464881Scg 8574363Scgstatic void 8674363Scgfeeder_unregisterall(void *p) 8774363Scg{ 8874363Scg struct feedertab_entry *fte, *next; 8974363Scg 9074363Scg next = SLIST_FIRST(&feedertab); 9174363Scg while (next != NULL) { 9274363Scg fte = next; 9374363Scg next = SLIST_NEXT(fte, link); 9474363Scg free(fte, M_FEEDER); 9574363Scg } 9674363Scg} 9774363Scg 9850724Scgstatic int 9964881Scgcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 10050724Scg{ 10166308Scg return ((n->type == m->type) && 10266308Scg ((n->in == 0) || (n->in == m->in)) && 10366308Scg ((n->out == 0) || (n->out == m->out)) && 10466308Scg (n->flags == m->flags)); 10550724Scg} 10650724Scg 10770134Scgstatic void 10874763Scgfeeder_destroy(struct pcm_feeder *f) 10950724Scg{ 11070134Scg FEEDER_FREE(f); 11170134Scg free(f->desc, M_FEEDER); 11270134Scg kobj_delete((kobj_t)f, M_FEEDER); 11370134Scg} 11464881Scg 11574763Scgstatic struct pcm_feeder * 11670134Scgfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 11770134Scg{ 11874763Scg struct pcm_feeder *f; 11970134Scg int err; 12070134Scg 12174763Scg f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO); 12270134Scg f->align = fc->align; 12370134Scg f->desc = malloc(sizeof(*(f->desc)), M_FEEDER, M_WAITOK | M_ZERO); 12470134Scg if (desc) 12570134Scg *(f->desc) = *desc; 12670134Scg else { 12770134Scg f->desc->type = FEEDER_ROOT; 12870134Scg f->desc->in = 0; 12970134Scg f->desc->out = 0; 13070134Scg f->desc->flags = 0; 13170134Scg f->desc->idx = 0; 13264881Scg } 13370134Scg f->data = fc->data; 13470134Scg f->source = NULL; 13570134Scg err = FEEDER_INIT(f); 13670134Scg if (err) { 13770134Scg feeder_destroy(f); 13870134Scg return NULL; 13970134Scg } else 14070134Scg return f; 14150724Scg} 14250724Scg 14370134Scgstruct feeder_class * 14470134Scgfeeder_getclass(struct pcm_feederdesc *desc) 14550724Scg{ 14664881Scg struct feedertab_entry *fte; 14750724Scg 14864881Scg SLIST_FOREACH(fte, &feedertab, link) { 14970134Scg if ((desc == NULL) && (fte->desc == NULL)) 15070134Scg return fte->feederclass; 15170134Scg if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 15270134Scg return fte->feederclass; 15364881Scg } 15464881Scg return NULL; 15550724Scg} 15650724Scg 15750724Scgint 15874763Scgchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 15966308Scg{ 16074763Scg struct pcm_feeder *nf; 16170134Scg 16270134Scg nf = feeder_create(fc, desc); 16370134Scg if (nf == NULL) 16466308Scg return -1; 16566308Scg 16670134Scg nf->source = c->feeder; 16766308Scg 16866308Scg if (nf->align > 0) 16966308Scg c->align += nf->align; 17066308Scg else if (nf->align < 0 && c->align < -nf->align) 17166308Scg c->align = -nf->align; 17266308Scg 17366308Scg c->feeder = nf; 17466308Scg 17566308Scg return 0; 17666308Scg} 17766308Scg 17866308Scgint 17974763Scgchn_removefeeder(struct pcm_channel *c) 18050724Scg{ 18174763Scg struct pcm_feeder *f; 18250724Scg 18370134Scg if (c->feeder == NULL) 18464881Scg return -1; 18570134Scg f = c->feeder; 18670134Scg c->feeder = c->feeder->source; 18770134Scg feeder_destroy(f); 18864881Scg return 0; 18950724Scg} 19050724Scg 19174763Scgstruct pcm_feeder * 19274763Scgchn_findfeeder(struct pcm_channel *c, u_int32_t type) 19366308Scg{ 19474763Scg struct pcm_feeder *f; 19566308Scg 19666308Scg f = c->feeder; 19766308Scg while (f != NULL) { 19866308Scg if (f->desc->type == type) 19966308Scg return f; 20066308Scg f = f->source; 20166308Scg } 20266308Scg return NULL; 20366308Scg} 20466308Scg 20550724Scgstatic int 20674763Scgchainok(struct pcm_feeder *test, struct pcm_feeder *stop) 20750724Scg{ 20864881Scg u_int32_t visited[MAXFEEDERS / 32]; 20964881Scg u_int32_t idx, mask; 21064881Scg 21164881Scg bzero(visited, sizeof(visited)); 21264881Scg while (test && (test != stop)) { 21364881Scg idx = test->desc->idx; 21464881Scg if (idx < 0) 21564881Scg panic("bad idx %d", idx); 21664881Scg if (idx >= MAXFEEDERS) 21764881Scg panic("bad idx %d", idx); 21864881Scg mask = 1 << (idx & 31); 21964881Scg idx >>= 5; 22064881Scg if (visited[idx] & mask) 22164881Scg return 0; 22264881Scg visited[idx] |= mask; 22364881Scg test = test->source; 22464881Scg } 22564881Scg return 1; 22650724Scg} 22750724Scg 22874763Scgstatic struct pcm_feeder * 22974763Scgfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 23050724Scg{ 23164881Scg struct feedertab_entry *fte; 23274763Scg struct pcm_feeder *try, *ret; 23370134Scg struct pcm_feederdesc trydesc; 23464881Scg 23564881Scg /* printf("trying %s...\n", source->name); */ 23664881Scg if (fmtvalid(source->desc->out, to)) { 23764881Scg /* printf("got it\n"); */ 23864881Scg return source; 23964881Scg } 24064881Scg 24164881Scg if (maxdepth < 0) 24264881Scg return NULL; 24364881Scg 24470134Scg trydesc.type = FEEDER_FMT; 24570134Scg trydesc.in = source->desc->out; 24670134Scg trydesc.out = 0; 24770134Scg trydesc.flags = 0; 24870134Scg trydesc.idx = -1; 24964881Scg 25064881Scg SLIST_FOREACH(fte, &feedertab, link) { 25164881Scg if ((fte->desc) && (fte->desc->in == source->desc->out)) { 25270134Scg trydesc.out = fte->desc->out; 25370134Scg trydesc.idx = fte->idx; 25470134Scg try = feeder_create(fte->feederclass, &trydesc); 25570134Scg if (try == NULL) 25670134Scg return NULL; 25764881Scg try->source = source; 25864881Scg ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 25964881Scg if (ret != NULL) 26064881Scg return ret; 26170134Scg feeder_destroy(try); 26264881Scg } 26364881Scg } 26464881Scg /* printf("giving up %s...\n", source->name); */ 26564881Scg return NULL; 26650724Scg} 26750724Scg 26864881Scgu_int32_t 26974763Scgchn_fmtchain(struct pcm_channel *c, u_int32_t *to) 27064881Scg{ 27174763Scg struct pcm_feeder *try, *stop; 27264881Scg int max; 27364881Scg 27464881Scg stop = c->feeder; 27564881Scg try = NULL; 27664881Scg max = 0; 27765645Scg while (try == NULL && max < 8) { 27864881Scg try = feeder_fmtchain(to, c->feeder, stop, max); 27965645Scg max++; 28065645Scg } 28164881Scg if (try == NULL) 28264881Scg return 0; 28364881Scg c->feeder = try; 28464881Scg c->align = 0; 28565645Scg#ifdef FEEDER_DEBUG 28665645Scg printf("chain: "); 28765645Scg#endif 28864881Scg while (try && (try != stop)) { 28965645Scg#ifdef FEEDER_DEBUG 29065645Scg printf("%s [%d]", try->name, try->desc->idx); 29165645Scg if (try->source) 29265645Scg printf(" -> "); 29365645Scg#endif 29464881Scg if (try->align > 0) 29564881Scg c->align += try->align; 29664881Scg else if (try->align < 0 && c->align < -try->align) 29764881Scg c->align = -try->align; 29864881Scg try = try->source; 29964881Scg } 30065645Scg#ifdef FEEDER_DEBUG 30165645Scg printf("%s [%d]\n", try->name, try->desc->idx); 30265645Scg#endif 30364881Scg return c->feeder->desc->out; 30464881Scg} 30566308Scg 30666308Scg/*****************************************************************************/ 30766308Scg 30866308Scgstatic int 30974763Scgfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 31066308Scg{ 31174763Scg struct snd_dbuf *src = source; 31274763Scg int l; 31374763Scg u_int8_t x; 31466308Scg 31574763Scg KASSERT(count > 0, ("feed_root: count == 0")); 31674763Scg /* count &= ~((1 << ch->align) - 1); */ 31774763Scg KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 31866308Scg 31974763Scg l = min(count, sndbuf_getready(src)); 32074763Scg sndbuf_dispose(src, buffer, l); 32166308Scg 32274763Scg/* 32374763Scg if (l < count) 32474763Scg printf("appending %d bytes\n", count - l); 32574763Scg*/ 32674763Scg 32774763Scg x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80; 32874763Scg while (l < count) 32974763Scg buffer[l++] = x; 33074763Scg 33166308Scg return count; 33266308Scg} 33370134Scg 33470134Scgstatic kobj_method_t feeder_root_methods[] = { 33570134Scg KOBJMETHOD(feeder_feed, feed_root), 33670134Scg { 0, 0 } 33766308Scg}; 33870134Scgstatic struct feeder_class feeder_root_class = { 33970134Scg name: "feeder_root", 34070134Scg methods: feeder_root_methods, 34174763Scg size: sizeof(struct pcm_feeder), 34270134Scg align: 0, 34370134Scg desc: NULL, 34470134Scg data: NULL, 34570134Scg}; 34670134ScgSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 34774363ScgSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 34866308Scg 34966308Scg 35066308Scg 35166308Scg 35270134Scg 353