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: src/sys/dev/sound/pcm/feeder.c,v 1.8.2.9 2003/02/08 01:43:07 orion Exp $ 27 * $DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.3 2003/11/15 21:05:42 dillon Exp $ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 32 #include "feeder_if.h" 33 34 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.3 2003/11/15 21:05:42 dillon Exp $"); 35 36 MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); 37 38 #define MAXFEEDERS 256 39 #undef FEEDER_DEBUG 40 41 struct feedertab_entry { 42 SLIST_ENTRY(feedertab_entry) link; 43 struct feeder_class *feederclass; 44 struct pcm_feederdesc *desc; 45 46 int idx; 47 }; 48 static SLIST_HEAD(, feedertab_entry) feedertab; 49 50 /*****************************************************************************/ 51 52 void 53 feeder_register(void *p) 54 { 55 static int feedercnt = 0; 56 57 struct feeder_class *fc = p; 58 struct feedertab_entry *fte; 59 int i; 60 61 if (feedercnt == 0) { 62 KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); 63 64 SLIST_INIT(&feedertab); 65 fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 66 if (fte == NULL) { 67 printf("can't allocate memory for root feeder\n"); 68 return; 69 } 70 fte->feederclass = fc; 71 fte->desc = NULL; 72 fte->idx = feedercnt; 73 SLIST_INSERT_HEAD(&feedertab, fte, link); 74 feedercnt++; 75 76 /* we've got our root feeder so don't veto pcm loading anymore */ 77 pcm_veto_load = 0; 78 79 return; 80 } 81 82 KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); 83 84 /* beyond this point failure is non-fatal but may result in some translations being unavailable */ 85 i = 0; 86 while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { 87 /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ 88 fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); 89 if (fte == NULL) { 90 printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); 91 92 return; 93 } 94 fte->feederclass = fc; 95 fte->desc = &fc->desc[i]; 96 fte->idx = feedercnt; 97 fte->desc->idx = feedercnt; 98 SLIST_INSERT_HEAD(&feedertab, fte, link); 99 i++; 100 } 101 feedercnt++; 102 if (feedercnt >= MAXFEEDERS) 103 printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); 104 } 105 106 static void 107 feeder_unregisterall(void *p) 108 { 109 struct feedertab_entry *fte, *next; 110 111 next = SLIST_FIRST(&feedertab); 112 while (next != NULL) { 113 fte = next; 114 next = SLIST_NEXT(fte, link); 115 free(fte, M_FEEDER); 116 } 117 } 118 119 static int 120 cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) 121 { 122 return ((n->type == m->type) && 123 ((n->in == 0) || (n->in == m->in)) && 124 ((n->out == 0) || (n->out == m->out)) && 125 (n->flags == m->flags)); 126 } 127 128 static void 129 feeder_destroy(struct pcm_feeder *f) 130 { 131 FEEDER_FREE(f); 132 kobj_delete((kobj_t)f, M_FEEDER); 133 } 134 135 static struct pcm_feeder * 136 feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) 137 { 138 struct pcm_feeder *f; 139 int err; 140 141 f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO); 142 if (f == NULL) 143 return NULL; 144 145 f->align = fc->align; 146 f->data = fc->data; 147 f->source = NULL; 148 f->parent = NULL; 149 f->class = fc; 150 f->desc = &(f->desc_static); 151 152 if (desc) { 153 *(f->desc) = *desc; 154 } else { 155 f->desc->type = FEEDER_ROOT; 156 f->desc->in = 0; 157 f->desc->out = 0; 158 f->desc->flags = 0; 159 f->desc->idx = 0; 160 } 161 162 err = FEEDER_INIT(f); 163 if (err) { 164 printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); 165 feeder_destroy(f); 166 167 return NULL; 168 } 169 170 return f; 171 } 172 173 struct feeder_class * 174 feeder_getclass(struct pcm_feederdesc *desc) 175 { 176 struct feedertab_entry *fte; 177 178 SLIST_FOREACH(fte, &feedertab, link) { 179 if ((desc == NULL) && (fte->desc == NULL)) 180 return fte->feederclass; 181 if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) 182 return fte->feederclass; 183 } 184 return NULL; 185 } 186 187 int 188 chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) 189 { 190 struct pcm_feeder *nf; 191 192 nf = feeder_create(fc, desc); 193 if (nf == NULL) 194 return ENOSPC; 195 196 nf->source = c->feeder; 197 198 if (nf->align > 0) 199 c->align += nf->align; 200 else if (nf->align < 0 && c->align < -nf->align) 201 c->align = -nf->align; 202 203 c->feeder = nf; 204 205 return 0; 206 } 207 208 int 209 chn_removefeeder(struct pcm_channel *c) 210 { 211 struct pcm_feeder *f; 212 213 if (c->feeder == NULL) 214 return -1; 215 f = c->feeder; 216 c->feeder = c->feeder->source; 217 feeder_destroy(f); 218 219 return 0; 220 } 221 222 struct pcm_feeder * 223 chn_findfeeder(struct pcm_channel *c, u_int32_t type) 224 { 225 struct pcm_feeder *f; 226 227 f = c->feeder; 228 while (f != NULL) { 229 if (f->desc->type == type) 230 return f; 231 f = f->source; 232 } 233 234 return NULL; 235 } 236 237 static int 238 chainok(struct pcm_feeder *test, struct pcm_feeder *stop) 239 { 240 u_int32_t visited[MAXFEEDERS / 32]; 241 u_int32_t idx, mask; 242 243 bzero(visited, sizeof(visited)); 244 while (test && (test != stop)) { 245 idx = test->desc->idx; 246 if (idx < 0) 247 panic("bad idx %d", idx); 248 if (idx >= MAXFEEDERS) 249 panic("bad idx %d", idx); 250 mask = 1 << (idx & 31); 251 idx >>= 5; 252 if (visited[idx] & mask) 253 return 0; 254 visited[idx] |= mask; 255 test = test->source; 256 } 257 258 return 1; 259 } 260 261 static struct pcm_feeder * 262 feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) 263 { 264 struct feedertab_entry *fte; 265 struct pcm_feeder *try, *ret; 266 267 /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ 268 if (fmtvalid(source->desc->out, to)) { 269 /* printf("got it\n"); */ 270 return source; 271 } 272 273 if (maxdepth < 0) 274 return NULL; 275 276 SLIST_FOREACH(fte, &feedertab, link) { 277 if (fte->desc == NULL) 278 continue; 279 if (fte->desc->type != FEEDER_FMT) 280 continue; 281 if (fte->desc->in == source->desc->out) { 282 try = feeder_create(fte->feederclass, fte->desc); 283 if (try) { 284 try->source = source; 285 ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; 286 if (ret != NULL) 287 return ret; 288 feeder_destroy(try); 289 } 290 } 291 } 292 /* printf("giving up %s...\n", source->class->name); */ 293 294 return NULL; 295 } 296 297 u_int32_t 298 chn_fmtchain(struct pcm_channel *c, u_int32_t *to) 299 { 300 struct pcm_feeder *try, *del, *stop; 301 u_int32_t tmpfrom[2], best, *from; 302 int i, max, bestmax; 303 304 KASSERT(c != NULL, ("c == NULL")); 305 KASSERT(c->feeder != NULL, ("c->feeder == NULL")); 306 KASSERT(to != NULL, ("to == NULL")); 307 KASSERT(to[0] != 0, ("to[0] == 0")); 308 309 stop = c->feeder; 310 311 if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { 312 from = chn_getcaps(c)->fmtlist; 313 } else { 314 tmpfrom[0] = c->feeder->desc->out; 315 tmpfrom[1] = 0; 316 from = tmpfrom; 317 } 318 319 i = 0; 320 best = 0; 321 bestmax = 100; 322 while (from[i] != 0) { 323 c->feeder->desc->out = from[i]; 324 try = NULL; 325 max = 0; 326 while (try == NULL && max < 8) { 327 try = feeder_fmtchain(to, c->feeder, stop, max); 328 if (try == NULL) 329 max++; 330 } 331 if (try != NULL && max < bestmax) { 332 bestmax = max; 333 best = from[i]; 334 } 335 while (try != NULL && try != stop) { 336 del = try; 337 try = try->source; 338 feeder_destroy(del); 339 } 340 i++; 341 } 342 if (best == 0) 343 return 0; 344 345 c->feeder->desc->out = best; 346 try = feeder_fmtchain(to, c->feeder, stop, bestmax); 347 if (try == NULL) 348 return 0; 349 350 c->feeder = try; 351 c->align = 0; 352 #ifdef FEEDER_DEBUG 353 printf("\n\nchain: "); 354 #endif 355 while (try && (try != stop)) { 356 #ifdef FEEDER_DEBUG 357 printf("%s [%d]", try->class->name, try->desc->idx); 358 if (try->source) 359 printf(" -> "); 360 #endif 361 if (try->source) 362 try->source->parent = try; 363 if (try->align > 0) 364 c->align += try->align; 365 else if (try->align < 0 && c->align < -try->align) 366 c->align = -try->align; 367 try = try->source; 368 } 369 #ifdef FEEDER_DEBUG 370 printf("%s [%d]\n", try->class->name, try->desc->idx); 371 #endif 372 373 return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out; 374 } 375 376 /*****************************************************************************/ 377 378 static int 379 feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) 380 { 381 struct snd_dbuf *src = source; 382 int l; 383 u_int8_t x; 384 385 KASSERT(count > 0, ("feed_root: count == 0")); 386 /* count &= ~((1 << ch->align) - 1); */ 387 KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); 388 389 l = min(count, sndbuf_getready(src)); 390 sndbuf_dispose(src, buffer, l); 391 392 /* When recording only return as much data as available */ 393 if (ch->direction == PCMDIR_REC) 394 return l; 395 396 /* 397 if (l < count) 398 printf("appending %d bytes\n", count - l); 399 */ 400 401 x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80; 402 while (l < count) 403 buffer[l++] = x; 404 405 return count; 406 } 407 408 static kobj_method_t feeder_root_methods[] = { 409 KOBJMETHOD(feeder_feed, feed_root), 410 { 0, 0 } 411 }; 412 static struct feeder_class feeder_root_class = { 413 name: "feeder_root", 414 methods: feeder_root_methods, 415 size: sizeof(struct pcm_feeder), 416 align: 0, 417 desc: NULL, 418 data: NULL, 419 }; 420 SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); 421 SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); 422 423 424 425 426 427