feeder.c revision 119853
1269369Sbr/* 2269369Sbr * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3269369Sbr * All rights reserved. 4269369Sbr * 5269369Sbr * Redistribution and use in source and binary forms, with or without 6269369Sbr * modification, are permitted provided that the following conditions 7269369Sbr * are met: 8269369Sbr * 1. Redistributions of source code must retain the above copyright 9269369Sbr * notice, this list of conditions and the following disclaimer. 10269369Sbr * 2. Redistributions in binary form must reproduce the above copyright 11269369Sbr * notice, this list of conditions and the following disclaimer in the 12269369Sbr * documentation and/or other materials provided with the distribution. 13269369Sbr * 14269369Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15269369Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16269369Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17269369Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18269369Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19269369Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20269369Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21269369Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22269369Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23269369Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24269369Sbr * SUCH DAMAGE. 25269369Sbr */ 26269369Sbr 27269369Sbr#include <dev/sound/pcm/sound.h> 28269369Sbr 29269369Sbr#include "feeder_if.h" 30269369Sbr 31269369SbrSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 119853 2003-09-07 16:28:03Z cg $"); 32269369Sbr 33269369SbrMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 34269369Sbr 35269369Sbr#define MAXFEEDERS 256 36269369Sbr#undef FEEDER_DEBUG 37269369Sbr 38269369Sbrstruct feedertab_entry { 39269369Sbr SLIST_ENTRY(feedertab_entry) link; 40269369Sbr struct feeder_class *feederclass; 41269369Sbr struct pcm_feederdesc *desc; 42269369Sbr 43269369Sbr int idx; 44269369Sbr}; 45269369Sbrstatic SLIST_HEAD(, feedertab_entry) feedertab; 46269369Sbr 47269369Sbr/*****************************************************************************/ 48269369Sbr 49269369Sbrvoid 50269369Sbrfeeder_register(void *p) 51269369Sbr{ 52269369Sbr static int feedercnt = 0; 53269369Sbr 54269369Sbr struct feeder_class *fc = p; 55269369Sbr struct feedertab_entry *fte; 56269369Sbr int i; 57269369Sbr 58269369Sbr if (feedercnt == 0) { 59269369Sbr KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 60269369Sbr 61269369Sbr SLIST_INIT(&feedertab); 62269369Sbr fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 63269369Sbr if (fte == NULL) { 64269369Sbr printf("can't allocate memory for root feeder: %s\n", 65269369Sbr fc->name); 66269369Sbr 67269369Sbr return; 68269369Sbr } 69269369Sbr fte->feederclass = fc; 70269369Sbr fte->desc = NULL; 71269369Sbr fte->idx = feedercnt; 72269369Sbr SLIST_INSERT_HEAD(&feedertab, fte, link); 73269369Sbr feedercnt++; 74269369Sbr 75269369Sbr /* we've got our root feeder so don't veto pcm loading anymore */ 76269369Sbr pcm_veto_load = 0; 77269369Sbr 78269369Sbr return; 79269369Sbr } 80269369Sbr 81269369Sbr KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 82269369Sbr 83269369Sbr /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 84269369Sbr i = 0; 85269369Sbr while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 86269369Sbr /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 87269369Sbr fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); 88269369Sbr if (fte == NULL) { 89269369Sbr printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 90269369Sbr 91269369Sbr return; 92279542Shselasky } 93279542Shselasky fte->feederclass = fc; 94279542Shselasky fte->desc = &fc->desc[i]; 95269369Sbr fte->idx = feedercnt; 96269369Sbr fte->desc->idx = feedercnt; 97269369Sbr SLIST_INSERT_HEAD(&feedertab, fte, link); 98269369Sbr i++; 99269369Sbr } 100269369Sbr feedercnt++; 101269369Sbr if (feedercnt >= MAXFEEDERS) 102269369Sbr printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 103269369Sbr} 104269369Sbr 105269369Sbrstatic void 106269369Sbrfeeder_unregisterall(void *p) 107269369Sbr{ 108269369Sbr struct feedertab_entry *fte, *next; 109269369Sbr 110269369Sbr next = SLIST_FIRST(&feedertab); 111269369Sbr while (next != NULL) { 112269369Sbr fte = next; 113269369Sbr next = SLIST_NEXT(fte, link); 114269369Sbr free(fte, M_FEEDER); 115269369Sbr } 116269369Sbr} 117269369Sbr 118269369Sbrstatic int 119269369Sbrcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 120269369Sbr{ 121269369Sbr return ((n->type == m->type) && 122269369Sbr ((n->in == 0) || (n->in == m->in)) && 123269369Sbr ((n->out == 0) || (n->out == m->out)) && 124269369Sbr (n->flags == m->flags)); 125269369Sbr} 126269369Sbr 127269369Sbrstatic void 128269369Sbrfeeder_destroy(struct pcm_feeder *f) 129269369Sbr{ 130269369Sbr FEEDER_FREE(f); 131269369Sbr kobj_delete((kobj_t)f, M_FEEDER); 132269369Sbr} 133269369Sbr 134269369Sbrstatic struct pcm_feeder * 135269369Sbrfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 136269369Sbr{ 137269369Sbr struct pcm_feeder *f; 138269369Sbr int err; 139269369Sbr 140269369Sbr f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); 141269369Sbr if (f == NULL) 142269369Sbr return NULL; 143269369Sbr 144269369Sbr f->align = fc->align; 145269369Sbr f->data = fc->data; 146269369Sbr f->source = NULL; 147269369Sbr f->parent = NULL; 148269369Sbr f->class = fc; 149269369Sbr f->desc = &(f->desc_static); 150269369Sbr 151269369Sbr if (desc) { 152269369Sbr *(f->desc) = *desc; 153269369Sbr } else { 154269369Sbr f->desc->type = FEEDER_ROOT; 155269369Sbr f->desc->in = 0; 156269369Sbr f->desc->out = 0; 157269369Sbr f->desc->flags = 0; 158269369Sbr f->desc->idx = 0; 159269369Sbr } 160269369Sbr 161269369Sbr err = FEEDER_INIT(f); 162269369Sbr if (err) { 163269369Sbr printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 164269369Sbr feeder_destroy(f); 165269369Sbr 166269369Sbr return NULL; 167269369Sbr } 168269369Sbr 169269369Sbr return f; 170269369Sbr} 171269369Sbr 172269369Sbrstruct feeder_class * 173269369Sbrfeeder_getclass(struct pcm_feederdesc *desc) 174269369Sbr{ 175269369Sbr struct feedertab_entry *fte; 176269369Sbr 177269369Sbr SLIST_FOREACH(fte, &feedertab, link) { 178269369Sbr if ((desc == NULL) && (fte->desc == NULL)) 179269369Sbr return fte->feederclass; 180269369Sbr if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 181269369Sbr return fte->feederclass; 182269369Sbr } 183269369Sbr return NULL; 184269369Sbr} 185269369Sbr 186269369Sbrint 187269369Sbrchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 188269369Sbr{ 189269369Sbr struct pcm_feeder *nf; 190269369Sbr 191269369Sbr nf = feeder_create(fc, desc); 192269369Sbr if (nf == NULL) 193269369Sbr return ENOSPC; 194269369Sbr 195269369Sbr nf->source = c->feeder; 196269369Sbr 197269369Sbr /* XXX we should use the lowest common denominator for align */ 198269369Sbr if (nf->align > 0) 199269369Sbr c->align += nf->align; 200269369Sbr else if (nf->align < 0 && c->align < -nf->align) 201269369Sbr c->align = -nf->align; 202269369Sbr if (c->feeder != NULL) 203269369Sbr c->feeder->parent = nf; 204269369Sbr c->feeder = nf; 205269369Sbr 206269369Sbr return 0; 207269369Sbr} 208269369Sbr 209269369Sbrint 210279542Shselaskychn_removefeeder(struct pcm_channel *c) 211269369Sbr{ 212269369Sbr struct pcm_feeder *f; 213269369Sbr 214269369Sbr if (c->feeder == NULL) 215269369Sbr return -1; 216269369Sbr f = c->feeder; 217269369Sbr c->feeder = c->feeder->source; 218269369Sbr feeder_destroy(f); 219269369Sbr 220269369Sbr return 0; 221269369Sbr} 222279542Shselasky 223269369Sbrstruct pcm_feeder * 224279542Shselaskychn_findfeeder(struct pcm_channel *c, u_int32_t type) 225269369Sbr{ 226269369Sbr struct pcm_feeder *f; 227269369Sbr 228269369Sbr f = c->feeder; 229269369Sbr while (f != NULL) { 230269369Sbr if (f->desc->type == type) 231269369Sbr return f; 232269369Sbr f = f->source; 233269369Sbr } 234279542Shselasky 235279542Shselasky return NULL; 236279542Shselasky} 237279542Shselasky 238279542Shselaskystatic int 239269369Sbrchainok(struct pcm_feeder *test, struct pcm_feeder *stop) 240279542Shselasky{ 241269369Sbr u_int32_t visited[MAXFEEDERS / 32]; 242279544Shselasky u_int32_t idx, mask; 243279542Shselasky 244279542Shselasky bzero(visited, sizeof(visited)); 245279542Shselasky while (test && (test != stop)) { 246279542Shselasky idx = test->desc->idx; 247279542Shselasky if (idx < 0) 248269369Sbr panic("bad idx %d", idx); 249269369Sbr if (idx >= MAXFEEDERS) 250279542Shselasky panic("bad idx %d", idx); 251279542Shselasky mask = 1 << (idx & 31); 252269369Sbr idx >>= 5; 253269369Sbr if (visited[idx] & mask) 254279542Shselasky return 0; 255279542Shselasky visited[idx] |= mask; 256269369Sbr test = test->source; 257269369Sbr } 258269369Sbr 259279542Shselasky return 1; 260279542Shselasky} 261269369Sbr 262279542Shselaskystatic struct pcm_feeder * 263269369Sbrfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 264279542Shselasky{ 265279542Shselasky struct feedertab_entry *fte; 266269369Sbr struct pcm_feeder *try, *ret; 267269369Sbr 268269369Sbr /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ 269279542Shselasky if (fmtvalid(source->desc->out, to)) { 270269369Sbr /* printf("got it\n"); */ 271269369Sbr return source; 272279542Shselasky } 273269369Sbr 274269369Sbr if (maxdepth < 0) 275269369Sbr return NULL; 276279542Shselasky 277269369Sbr SLIST_FOREACH(fte, &feedertab, link) { 278279542Shselasky if (fte->desc == NULL) 279279542Shselasky continue; 280279542Shselasky if (fte->desc->type != FEEDER_FMT) 281269369Sbr continue; 282279542Shselasky if (fte->desc->in == source->desc->out) { 283279542Shselasky try = feeder_create(fte->feederclass, fte->desc); 284279542Shselasky if (try) { 285269369Sbr try->source = source; 286269369Sbr ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 287269369Sbr if (ret != NULL) 288269369Sbr return ret; 289269369Sbr feeder_destroy(try); 290279542Shselasky } 291279542Shselasky } 292269369Sbr } 293269369Sbr /* printf("giving up %s...\n", source->class->name); */ 294279542Shselasky 295279542Shselasky return NULL; 296279542Shselasky} 297279542Shselasky 298279542Shselaskyu_int32_t 299279542Shselaskychn_fmtchain(struct pcm_channel *c, u_int32_t *to) 300279542Shselasky{ 301269369Sbr struct pcm_feeder *try, *del, *stop; 302279542Shselasky u_int32_t tmpfrom[2], best, *from; 303279542Shselasky int i, max, bestmax; 304279542Shselasky 305269369Sbr KASSERT(c != NULL, ("c == NULL")); 306279542Shselasky KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 307269369Sbr KASSERT(to != NULL, ("to == NULL")); 308279542Shselasky KASSERT(to[0] != 0, ("to[0] == 0")); 309269369Sbr 310269369Sbr stop = c->feeder; 311269369Sbr 312269369Sbr if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 313269369Sbr from = chn_getcaps(c)->fmtlist; 314269369Sbr } else { 315269369Sbr tmpfrom[0] = c->feeder->desc->out; 316279542Shselasky tmpfrom[1] = 0; 317279542Shselasky from = tmpfrom; 318269369Sbr } 319269369Sbr 320 i = 0; 321 best = 0; 322 bestmax = 100; 323 while (from[i] != 0) { 324 c->feeder->desc->out = from[i]; 325 try = NULL; 326 max = 0; 327 while (try == NULL && max < 8) { 328 try = feeder_fmtchain(to, c->feeder, stop, max); 329 if (try == NULL) 330 max++; 331 } 332 if (try != NULL && max < bestmax) { 333 bestmax = max; 334 best = from[i]; 335 } 336 while (try != NULL && try != stop) { 337 del = try; 338 try = try->source; 339 feeder_destroy(del); 340 } 341 i++; 342 } 343 if (best == 0) 344 return 0; 345 346 c->feeder->desc->out = best; 347 try = feeder_fmtchain(to, c->feeder, stop, bestmax); 348 if (try == NULL) 349 return 0; 350 351 c->feeder = try; 352 c->align = 0; 353#ifdef FEEDER_DEBUG 354 printf("\n\nchain: "); 355#endif 356 while (try && (try != stop)) { 357#ifdef FEEDER_DEBUG 358 printf("%s [%d]", try->class->name, try->desc->idx); 359 if (try->source) 360 printf(" -> "); 361#endif 362 if (try->source) 363 try->source->parent = try; 364 if (try->align > 0) 365 c->align += try->align; 366 else if (try->align < 0 && c->align < -try->align) 367 c->align = -try->align; 368 try = try->source; 369 } 370#ifdef FEEDER_DEBUG 371 printf("%s [%d]\n", try->class->name, try->desc->idx); 372#endif 373 374 return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out; 375} 376 377void 378feeder_printchain(struct pcm_feeder *head) 379{ 380 struct pcm_feeder *f; 381 382 printf("feeder chain (head @%p)\n", head); 383 f = head; 384 while (f != NULL) { 385 printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); 386 f = f->source; 387 } 388 printf("[end]\n\n"); 389} 390 391/*****************************************************************************/ 392 393static int 394feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 395{ 396 struct snd_dbuf *src = source; 397 int l; 398 u_int8_t x; 399 400 KASSERT(count > 0, ("feed_root: count == 0")); 401 /* count &= ~((1 << ch->align) - 1); */ 402 KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 403 404 l = min(count, sndbuf_getready(src)); 405 sndbuf_dispose(src, buffer, l); 406 407 /* When recording only return as much data as available */ 408 if (ch->direction == PCMDIR_REC) 409 return l; 410 411/* 412 if (l < count) 413 printf("appending %d bytes\n", count - l); 414*/ 415 416 x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80; 417 while (l < count) 418 buffer[l++] = x; 419 420 return count; 421} 422 423static kobj_method_t feeder_root_methods[] = { 424 KOBJMETHOD(feeder_feed, feed_root), 425 { 0, 0 } 426}; 427static struct feeder_class feeder_root_class = { 428 .name = "feeder_root", 429 .methods = feeder_root_methods, 430 .size = sizeof(struct pcm_feeder), 431 .align = 0, 432 .desc = NULL, 433 .data = NULL, 434}; 435SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 436SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 437 438 439 440 441 442