feeder.c revision 70134
1/* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/sound/pcm/feeder.c 70134 2000-12-18 01:36:41Z cg $ 27 */ 28 29#include <dev/sound/pcm/sound.h> 30 31#include "feeder_if.h" 32 33MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 34 35#define MAXFEEDERS 256 36#undef FEEDER_DEBUG 37 38struct feedertab_entry { 39 SLIST_ENTRY(feedertab_entry) link; 40 struct feeder_class *feederclass; 41 struct pcm_feederdesc *desc; 42 43 int idx; 44}; 45static SLIST_HEAD(, feedertab_entry) feedertab; 46 47/*****************************************************************************/ 48 49void 50feeder_register(void *p) 51{ 52 struct feeder_class *fc = p; 53 struct feedertab_entry *fte; 54 static int feedercnt = 0; 55 int i; 56 57 if (feedercnt == 0) { 58 if (fc->desc) 59 panic("FIRST FEEDER NOT ROOT: %s\n", fc->name); 60 SLIST_INIT(&feedertab); 61 fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 62 fte->feederclass = fc; 63 fte->desc = NULL; 64 fte->idx = feedercnt; 65 SLIST_INSERT_HEAD(&feedertab, fte, link); 66 feedercnt++; 67 return; 68 } 69 /* printf("installing feeder: %s\n", f->name); */ 70 71 i = 0; 72 while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 73 fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 74 fte->feederclass = fc; 75 fte->desc = &fc->desc[i]; 76 fte->idx = feedercnt; 77 fte->desc->idx = feedercnt; 78 SLIST_INSERT_HEAD(&feedertab, fte, link); 79 i++; 80 } 81 feedercnt++; 82 if (feedercnt >= MAXFEEDERS) 83 printf("MAXFEEDERS exceeded\n"); 84} 85 86static int 87cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 88{ 89 return ((n->type == m->type) && 90 ((n->in == 0) || (n->in == m->in)) && 91 ((n->out == 0) || (n->out == m->out)) && 92 (n->flags == m->flags)); 93} 94 95static void 96feeder_destroy(pcm_feeder *f) 97{ 98 FEEDER_FREE(f); 99 free(f->desc, M_FEEDER); 100 kobj_delete((kobj_t)f, M_FEEDER); 101} 102 103static pcm_feeder * 104feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 105{ 106 pcm_feeder *f; 107 int err; 108 109 f = (pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO); 110 f->align = fc->align; 111 f->desc = malloc(sizeof(*(f->desc)), M_FEEDER, M_WAITOK | M_ZERO); 112 if (desc) 113 *(f->desc) = *desc; 114 else { 115 f->desc->type = FEEDER_ROOT; 116 f->desc->in = 0; 117 f->desc->out = 0; 118 f->desc->flags = 0; 119 f->desc->idx = 0; 120 } 121 f->data = fc->data; 122 f->source = NULL; 123 err = FEEDER_INIT(f); 124 if (err) { 125 feeder_destroy(f); 126 return NULL; 127 } else 128 return f; 129} 130 131struct feeder_class * 132feeder_getclass(struct pcm_feederdesc *desc) 133{ 134 struct feedertab_entry *fte; 135 136 SLIST_FOREACH(fte, &feedertab, link) { 137 if ((desc == NULL) && (fte->desc == NULL)) 138 return fte->feederclass; 139 if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 140 return fte->feederclass; 141 } 142 return NULL; 143} 144 145int 146chn_addfeeder(pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 147{ 148 pcm_feeder *nf; 149 150 nf = feeder_create(fc, desc); 151 if (nf == NULL) 152 return -1; 153 154 nf->source = c->feeder; 155 156 if (nf->align > 0) 157 c->align += nf->align; 158 else if (nf->align < 0 && c->align < -nf->align) 159 c->align = -nf->align; 160 161 c->feeder = nf; 162 163 return 0; 164} 165 166int 167chn_removefeeder(pcm_channel *c) 168{ 169 pcm_feeder *f; 170 171 if (c->feeder == NULL) 172 return -1; 173 f = c->feeder; 174 c->feeder = c->feeder->source; 175 feeder_destroy(f); 176 return 0; 177} 178 179pcm_feeder * 180chn_findfeeder(pcm_channel *c, u_int32_t type) 181{ 182 pcm_feeder *f; 183 184 f = c->feeder; 185 while (f != NULL) { 186 if (f->desc->type == type) 187 return f; 188 f = f->source; 189 } 190 return NULL; 191} 192 193static int 194chainok(pcm_feeder *test, pcm_feeder *stop) 195{ 196 u_int32_t visited[MAXFEEDERS / 32]; 197 u_int32_t idx, mask; 198 199 bzero(visited, sizeof(visited)); 200 while (test && (test != stop)) { 201 idx = test->desc->idx; 202 if (idx < 0) 203 panic("bad idx %d", idx); 204 if (idx >= MAXFEEDERS) 205 panic("bad idx %d", idx); 206 mask = 1 << (idx & 31); 207 idx >>= 5; 208 if (visited[idx] & mask) 209 return 0; 210 visited[idx] |= mask; 211 test = test->source; 212 } 213 return 1; 214} 215 216static pcm_feeder * 217feeder_fmtchain(u_int32_t *to, pcm_feeder *source, pcm_feeder *stop, int maxdepth) 218{ 219 struct feedertab_entry *fte; 220 pcm_feeder *try, *ret; 221 struct pcm_feederdesc trydesc; 222 223 /* printf("trying %s...\n", source->name); */ 224 if (fmtvalid(source->desc->out, to)) { 225 /* printf("got it\n"); */ 226 return source; 227 } 228 229 if (maxdepth < 0) 230 return NULL; 231 232 trydesc.type = FEEDER_FMT; 233 trydesc.in = source->desc->out; 234 trydesc.out = 0; 235 trydesc.flags = 0; 236 trydesc.idx = -1; 237 238 SLIST_FOREACH(fte, &feedertab, link) { 239 if ((fte->desc) && (fte->desc->in == source->desc->out)) { 240 trydesc.out = fte->desc->out; 241 trydesc.idx = fte->idx; 242 try = feeder_create(fte->feederclass, &trydesc); 243 if (try == NULL) 244 return NULL; 245 try->source = source; 246 ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 247 if (ret != NULL) 248 return ret; 249 feeder_destroy(try); 250 } 251 } 252 /* printf("giving up %s...\n", source->name); */ 253 return NULL; 254} 255 256u_int32_t 257chn_fmtchain(pcm_channel *c, u_int32_t *to) 258{ 259 pcm_feeder *try, *stop; 260 int max; 261 262 stop = c->feeder; 263 try = NULL; 264 max = 0; 265 while (try == NULL && max < 8) { 266 try = feeder_fmtchain(to, c->feeder, stop, max); 267 max++; 268 } 269 if (try == NULL) 270 return 0; 271 c->feeder = try; 272 c->align = 0; 273#ifdef FEEDER_DEBUG 274 printf("chain: "); 275#endif 276 while (try && (try != stop)) { 277#ifdef FEEDER_DEBUG 278 printf("%s [%d]", try->name, try->desc->idx); 279 if (try->source) 280 printf(" -> "); 281#endif 282 if (try->align > 0) 283 c->align += try->align; 284 else if (try->align < 0 && c->align < -try->align) 285 c->align = -try->align; 286 try = try->source; 287 } 288#ifdef FEEDER_DEBUG 289 printf("%s [%d]\n", try->name, try->desc->idx); 290#endif 291 return c->feeder->desc->out; 292} 293 294/*****************************************************************************/ 295 296static int 297feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream) 298{ 299 int ret, s; 300 301 KASSERT(count, ("feed_root: count == 0")); 302 count &= ~((1 << ch->align) - 1); 303 KASSERT(count, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 304 305 s = spltty(); 306 count = min(count, stream->uio_resid); 307 if (count) { 308 ret = uiomove(buffer, count, stream); 309 KASSERT(ret == 0, ("feed_root: uiomove failed (%d)", ret)); 310 } 311 splx(s); 312 313 return count; 314} 315 316static kobj_method_t feeder_root_methods[] = { 317 KOBJMETHOD(feeder_feed, feed_root), 318 { 0, 0 } 319}; 320static struct feeder_class feeder_root_class = { 321 name: "feeder_root", 322 methods: feeder_root_methods, 323 size: sizeof(pcm_feeder), 324 align: 0, 325 desc: NULL, 326 data: NULL, 327}; 328SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 329 330 331 332 333 334