feeder.c revision 111119
1145519Sdarrenr/* 2145510Sdarrenr * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3145510Sdarrenr * All rights reserved. 4255332Scy * 5145510Sdarrenr * Redistribution and use in source and binary forms, with or without 6145510Sdarrenr * modification, are permitted provided that the following conditions 7145510Sdarrenr * are met: 8145510Sdarrenr * 1. Redistributions of source code must retain the above copyright 9145510Sdarrenr * notice, this list of conditions and the following disclaimer. 10145510Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright 11145510Sdarrenr * notice, this list of conditions and the following disclaimer in the 12145510Sdarrenr * documentation and/or other materials provided with the distribution. 13145510Sdarrenr * 14145510Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15145510Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16145510Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17145510Sdarrenr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18145510Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19145510Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20145510Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21145510Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22145510Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23145510Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24145510Sdarrenr * SUCH DAMAGE. 25145510Sdarrenr */ 26145510Sdarrenr 27170268Sdarrenr#include <dev/sound/pcm/sound.h> 28145510Sdarrenr 29145510Sdarrenr#include "feeder_if.h" 30170268Sdarrenr 31145510SdarrenrSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 111119 2003-02-19 05:47:46Z imp $"); 32145510Sdarrenr 33145510SdarrenrMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 34145510Sdarrenr 35145510Sdarrenr#define MAXFEEDERS 256 36145510Sdarrenr#undef FEEDER_DEBUG 37369245Sgit2svn 38145510Sdarrenrstruct feedertab_entry { 39145510Sdarrenr SLIST_ENTRY(feedertab_entry) link; 40145510Sdarrenr struct feeder_class *feederclass; 41145510Sdarrenr struct pcm_feederdesc *desc; 42145510Sdarrenr 43369245Sgit2svn int idx; 44369245Sgit2svn}; 45369245Sgit2svnstatic SLIST_HEAD(, feedertab_entry) feedertab; 46369245Sgit2svn 47369245Sgit2svn/*****************************************************************************/ 48369245Sgit2svn 49369245Sgit2svnvoid 50369245Sgit2svnfeeder_register(void *p) 51369245Sgit2svn{ 52369245Sgit2svn static int feedercnt = 0; 53369245Sgit2svn 54369245Sgit2svn struct feeder_class *fc = p; 55369245Sgit2svn struct feedertab_entry *fte; 56369245Sgit2svn int i; 57369245Sgit2svn 58369245Sgit2svn if (feedercnt == 0) { 59145510Sdarrenr KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 60145510Sdarrenr 61145510Sdarrenr SLIST_INIT(&feedertab); 62145510Sdarrenr fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 63255332Scy if (fte == NULL) { 64255332Scy printf("can't allocate memory for root feeder: %s\n", 65145510Sdarrenr fc->name); 66145510Sdarrenr 67255332Scy return; 68255332Scy } 69255332Scy fte->feederclass = fc; 70145510Sdarrenr fte->desc = NULL; 71145510Sdarrenr fte->idx = feedercnt; 72353089Scy SLIST_INSERT_HEAD(&feedertab, fte, link); 73255332Scy feedercnt++; 74353092Scy 75255332Scy /* we've got our root feeder so don't veto pcm loading anymore */ 76353091Scy pcm_veto_load = 0; 77255332Scy 78255332Scy return; 79255332Scy } 80145510Sdarrenr 81145510Sdarrenr KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 82145510Sdarrenr 83145510Sdarrenr /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 84255332Scy i = 0; 85255332Scy while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 86255332Scy /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 87255332Scy fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 88145510Sdarrenr if (fte == NULL) { 89255332Scy printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 90145510Sdarrenr 91145510Sdarrenr return; 92145510Sdarrenr } 93145510Sdarrenr fte->feederclass = fc; 94255332Scy fte->desc = &fc->desc[i]; 95255332Scy fte->idx = feedercnt; 96353083Scy fte->desc->idx = feedercnt; 97145510Sdarrenr SLIST_INSERT_HEAD(&feedertab, fte, link); 98145510Sdarrenr i++; 99145510Sdarrenr } 100145510Sdarrenr feedercnt++; 101145510Sdarrenr if (feedercnt >= MAXFEEDERS) 102145510Sdarrenr printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 103145510Sdarrenr} 104145510Sdarrenr 105145510Sdarrenrstatic void 106145510Sdarrenrfeeder_unregisterall(void *p) 107145510Sdarrenr{ 108145510Sdarrenr struct feedertab_entry *fte, *next; 109145510Sdarrenr 110145510Sdarrenr next = SLIST_FIRST(&feedertab); 111145510Sdarrenr while (next != NULL) { 112145510Sdarrenr fte = next; 113145510Sdarrenr next = SLIST_NEXT(fte, link); 114145510Sdarrenr free(fte, M_FEEDER); 115145510Sdarrenr } 116145510Sdarrenr} 117145510Sdarrenr 118145510Sdarrenrstatic int 119145510Sdarrenrcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 120145510Sdarrenr{ 121145510Sdarrenr return ((n->type == m->type) && 122145510Sdarrenr ((n->in == 0) || (n->in == m->in)) && 123145510Sdarrenr ((n->out == 0) || (n->out == m->out)) && 124145510Sdarrenr (n->flags == m->flags)); 125145510Sdarrenr} 126170268Sdarrenr 127170268Sdarrenrstatic void 128170268Sdarrenrfeeder_destroy(struct pcm_feeder *f) 129145510Sdarrenr{ 130145510Sdarrenr FEEDER_FREE(f); 131145510Sdarrenr kobj_delete((kobj_t)f, M_FEEDER); 132255332Scy} 133255332Scy 134255332Scystatic struct pcm_feeder * 135255332Scyfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 136145510Sdarrenr{ 137255332Scy struct pcm_feeder *f; 138170268Sdarrenr int err; 139255332Scy 140255332Scy f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); 141255332Scy if (f == NULL) 142145510Sdarrenr return NULL; 143145510Sdarrenr 144145510Sdarrenr f->align = fc->align; 145255332Scy f->data = fc->data; 146255332Scy f->source = NULL; 147145510Sdarrenr f->parent = NULL; 148353093Scy f->class = fc; 149145510Sdarrenr f->desc = &(f->desc_static); 150145510Sdarrenr 151145510Sdarrenr if (desc) { 152145510Sdarrenr *(f->desc) = *desc; 153145510Sdarrenr } else { 154145510Sdarrenr f->desc->type = FEEDER_ROOT; 155145510Sdarrenr f->desc->in = 0; 156255332Scy f->desc->out = 0; 157170268Sdarrenr f->desc->flags = 0; 158145510Sdarrenr f->desc->idx = 0; 159145510Sdarrenr } 160145510Sdarrenr 161145510Sdarrenr err = FEEDER_INIT(f); 162145510Sdarrenr if (err) { 163255332Scy printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 164145510Sdarrenr feeder_destroy(f); 165145510Sdarrenr 166255332Scy return NULL; 167255332Scy } 168255332Scy 169255332Scy return f; 170255332Scy} 171145510Sdarrenr 172145510Sdarrenrstruct feeder_class * 173145510Sdarrenrfeeder_getclass(struct pcm_feederdesc *desc) 174145510Sdarrenr{ 175255332Scy struct feedertab_entry *fte; 176255332Scy 177255332Scy SLIST_FOREACH(fte, &feedertab, link) { 178255332Scy if ((desc == NULL) && (fte->desc == NULL)) 179255332Scy return fte->feederclass; 180255332Scy if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 181255332Scy return fte->feederclass; 182255332Scy } 183255332Scy return NULL; 184255332Scy} 185255332Scy 186255332Scyint 187255332Scychn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 188255332Scy{ 189255332Scy struct pcm_feeder *nf; 190255332Scy 191255332Scy nf = feeder_create(fc, desc); 192255332Scy if (nf == NULL) 193255332Scy return ENOSPC; 194255332Scy 195255332Scy nf->source = c->feeder; 196255332Scy 197255332Scy if (nf->align > 0) 198255332Scy c->align += nf->align; 199255332Scy else if (nf->align < 0 && c->align < -nf->align) 200255332Scy c->align = -nf->align; 201145510Sdarrenr 202145510Sdarrenr c->feeder = nf; 203145510Sdarrenr 204353086Scy return 0; 205353086Scy} 206353086Scy 207145510Sdarrenrint 208145510Sdarrenrchn_removefeeder(struct pcm_channel *c) 209353086Scy{ 210353086Scy struct pcm_feeder *f; 211353086Scy 212170268Sdarrenr if (c->feeder == NULL) 213255332Scy return -1; 214170268Sdarrenr f = c->feeder; 215170268Sdarrenr c->feeder = c->feeder->source; 216170268Sdarrenr feeder_destroy(f); 217145510Sdarrenr 218145510Sdarrenr return 0; 219145510Sdarrenr} 220170268Sdarrenr 221170268Sdarrenrstruct pcm_feeder * 222145510Sdarrenrchn_findfeeder(struct pcm_channel *c, u_int32_t type) 223170268Sdarrenr{ 224170268Sdarrenr struct pcm_feeder *f; 225145510Sdarrenr 226145510Sdarrenr f = c->feeder; 227145510Sdarrenr while (f != NULL) { 228145510Sdarrenr if (f->desc->type == type) 229145510Sdarrenr return f; 230255332Scy f = f->source; 231255332Scy } 232255332Scy 233255332Scy return NULL; 234255332Scy} 235255332Scy 236255332Scystatic int 237255332Scychainok(struct pcm_feeder *test, struct pcm_feeder *stop) 238255332Scy{ 239255332Scy u_int32_t visited[MAXFEEDERS / 32]; 240255332Scy u_int32_t idx, mask; 241255332Scy 242255332Scy bzero(visited, sizeof(visited)); 243255332Scy while (test && (test != stop)) { 244255332Scy idx = test->desc->idx; 245255332Scy if (idx < 0) 246145510Sdarrenr panic("bad idx %d", idx); 247145510Sdarrenr if (idx >= MAXFEEDERS) 248145510Sdarrenr panic("bad idx %d", idx); 249145510Sdarrenr mask = 1 << (idx & 31); 250255332Scy idx >>= 5; 251255332Scy if (visited[idx] & mask) 252255332Scy return 0; 253255332Scy visited[idx] |= mask; 254145510Sdarrenr test = test->source; 255145510Sdarrenr } 256353094Scy 257145510Sdarrenr return 1; 258145510Sdarrenr} 259145510Sdarrenr 260145510Sdarrenrstatic struct pcm_feeder * 261145510Sdarrenrfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 262145510Sdarrenr{ 263145510Sdarrenr struct feedertab_entry *fte; 264145510Sdarrenr struct pcm_feeder *try, *ret; 265145510Sdarrenr 266145510Sdarrenr /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ 267145510Sdarrenr if (fmtvalid(source->desc->out, to)) { 268353094Scy /* printf("got it\n"); */ 269145510Sdarrenr return source; 270145510Sdarrenr } 271145510Sdarrenr 272145510Sdarrenr if (maxdepth < 0) 273145510Sdarrenr return NULL; 274145510Sdarrenr 275145510Sdarrenr SLIST_FOREACH(fte, &feedertab, link) { 276145510Sdarrenr if (fte->desc == NULL) 277145510Sdarrenr continue; 278145510Sdarrenr if (fte->desc->type != FEEDER_FMT) 279255332Scy continue; 280145510Sdarrenr if (fte->desc->in == source->desc->out) { 281145510Sdarrenr try = feeder_create(fte->feederclass, fte->desc); 282145510Sdarrenr if (try) { 283145510Sdarrenr try->source = source; 284145510Sdarrenr ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 285145510Sdarrenr if (ret != NULL) 286145510Sdarrenr return ret; 287145510Sdarrenr feeder_destroy(try); 288145510Sdarrenr } 289353087Scy } 290353087Scy } 291353087Scy /* printf("giving up %s...\n", source->class->name); */ 292353087Scy 293145510Sdarrenr return NULL; 294353094Scy} 295353094Scy 296353094Scyu_int32_t 297353094Scychn_fmtchain(struct pcm_channel *c, u_int32_t *to) 298145510Sdarrenr{ 299145510Sdarrenr struct pcm_feeder *try, *del, *stop; 300145510Sdarrenr u_int32_t tmpfrom[2], best, *from; 301353084Scy int i, max, bestmax; 302353084Scy 303353084Scy KASSERT(c != NULL, ("c == NULL")); 304145510Sdarrenr KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 305145510Sdarrenr KASSERT(to != NULL, ("to == NULL")); 306353084Scy KASSERT(to[0] != 0, ("to[0] == 0")); 307353084Scy 308353084Scy stop = c->feeder; 309145510Sdarrenr 310145510Sdarrenr if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 311145510Sdarrenr from = chn_getcaps(c)->fmtlist; 312145510Sdarrenr } else { 313145510Sdarrenr tmpfrom[0] = c->feeder->desc->out; 314145510Sdarrenr tmpfrom[1] = 0; 315145510Sdarrenr from = tmpfrom; 316145510Sdarrenr } 317353094Scy 318353094Scy i = 0; 319353094Scy best = 0; 320353094Scy bestmax = 100; 321353094Scy while (from[i] != 0) { 322353094Scy c->feeder->desc->out = from[i]; 323353094Scy try = NULL; 324255332Scy max = 0; 325255332Scy while (try == NULL && max < 8) { 326255332Scy try = feeder_fmtchain(to, c->feeder, stop, max); 327353094Scy if (try == NULL) 328145510Sdarrenr max++; 329145510Sdarrenr } 330145510Sdarrenr if (try != NULL && max < bestmax) { 331353094Scy bestmax = max; 332353094Scy best = from[i]; 333145510Sdarrenr } 334145510Sdarrenr while (try != NULL && try != stop) { 335145510Sdarrenr del = try; 336145510Sdarrenr try = try->source; 337145510Sdarrenr feeder_destroy(del); 338145510Sdarrenr } 339145510Sdarrenr i++; 340145510Sdarrenr } 341145510Sdarrenr if (best == 0) 342145510Sdarrenr return 0; 343145510Sdarrenr 344145510Sdarrenr c->feeder->desc->out = best; 345145510Sdarrenr try = feeder_fmtchain(to, c->feeder, stop, bestmax); 346145510Sdarrenr if (try == NULL) 347145510Sdarrenr return 0; 348145510Sdarrenr 349145510Sdarrenr c->feeder = try; 350145510Sdarrenr c->align = 0; 351145510Sdarrenr#ifdef FEEDER_DEBUG 352145510Sdarrenr printf("\n\nchain: "); 353145510Sdarrenr#endif 354145510Sdarrenr while (try && (try != stop)) { 355145510Sdarrenr#ifdef FEEDER_DEBUG 356145510Sdarrenr printf("%s [%d]", try->class->name, try->desc->idx); 357353094Scy if (try->source) 358353094Scy printf(" -> "); 359353094Scy#endif 360353094Scy if (try->source) 361353094Scy try->source->parent = try; 362353094Scy if (try->align > 0) 363353094Scy c->align += try->align; 364353094Scy else if (try->align < 0 && c->align < -try->align) 365353094Scy c->align = -try->align; 366353094Scy try = try->source; 367145510Sdarrenr } 368145510Sdarrenr#ifdef FEEDER_DEBUG 369145510Sdarrenr printf("%s [%d]\n", try->class->name, try->desc->idx); 370145510Sdarrenr#endif 371145510Sdarrenr 372145510Sdarrenr return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out; 373255332Scy} 374255332Scy 375255332Scy/*****************************************************************************/ 376255332Scy 377145510Sdarrenrstatic int 378145510Sdarrenrfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 379145510Sdarrenr{ 380358321Scy struct snd_dbuf *src = source; 381145510Sdarrenr int l; 382145510Sdarrenr u_int8_t x; 383145510Sdarrenr 384145510Sdarrenr KASSERT(count > 0, ("feed_root: count == 0")); 385145510Sdarrenr /* count &= ~((1 << ch->align) - 1); */ 386145510Sdarrenr KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 387358321Scy 388358321Scy l = min(count, sndbuf_getready(src)); 389358321Scy sndbuf_dispose(src, buffer, l); 390358321Scy 391145510Sdarrenr /* When recording only return as much data as available */ 392255332Scy if (ch->direction == PCMDIR_REC) 393145510Sdarrenr return l; 394145510Sdarrenr 395145510Sdarrenr/* 396145510Sdarrenr if (l < count) 397145510Sdarrenr printf("appending %d bytes\n", count - l); 398145510Sdarrenr*/ 399145510Sdarrenr 400353080Scy x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80; 401353080Scy while (l < count) 402353080Scy buffer[l++] = x; 403145510Sdarrenr 404145510Sdarrenr return count; 405353080Scy} 406353080Scy 407353080Scystatic kobj_method_t feeder_root_methods[] = { 408145510Sdarrenr KOBJMETHOD(feeder_feed, feed_root), 409145510Sdarrenr { 0, 0 } 410145510Sdarrenr}; 411255332Scystatic struct feeder_class feeder_root_class = { 412145510Sdarrenr name: "feeder_root", 413145510Sdarrenr methods: feeder_root_methods, 414145510Sdarrenr size: sizeof(struct pcm_feeder), 415145510Sdarrenr align: 0, 416145510Sdarrenr desc: NULL, 417145510Sdarrenr data: NULL, 418145510Sdarrenr}; 419145510SdarrenrSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 420145510SdarrenrSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 421145510Sdarrenr 422145510Sdarrenr 423145510Sdarrenr 424145510Sdarrenr 425255332Scy 426255332Scy