1 /*-
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * Portions Copyright by Luigi Rizzo - 1997-99
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <dev/sound/pcm/sound.h>
29
30 #include "feeder_if.h"
31
32 SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.97.2.1 2005/01/30 01:00:04 imp Exp $");
33
34 #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
35 #define DMA_ALIGN_THRESHOLD 4
36 #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
37
38 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
39
40 /*
41 #define DEB(x) x
42 */
43
44 static int chn_targetirqrate = 32;
45 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
46
47 static int
48 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
49 {
50 int err, val;
51
52 val = chn_targetirqrate;
53 err = sysctl_handle_int(oidp, &val, sizeof(val), req);
54 if (val < 16 || val > 512)
55 err = EINVAL;
56 else
57 chn_targetirqrate = val;
58
59 return err;
60 }
61 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
62 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
63 static int report_soft_formats = 1;
64 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
65 &report_soft_formats, 1, "report software-emulated formats");
66
67 static int chn_buildfeeder(struct pcm_channel *c);
68
69 static void
70 chn_lockinit(struct pcm_channel *c, int dir)
71 {
72 switch(dir) {
73 case PCMDIR_PLAY:
74 c->lock = snd_mtxcreate(c->name, "pcm play channel");
75 break;
76 case PCMDIR_REC:
77 c->lock = snd_mtxcreate(c->name, "pcm record channel");
78 break;
79 case PCMDIR_VIRTUAL:
80 c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
81 break;
82 case 0:
83 c->lock = snd_mtxcreate(c->name, "pcm fake channel");
84 break;
85 }
86 }
87
88 static void
89 chn_lockdestroy(struct pcm_channel *c)
90 {
91 snd_mtxfree(c->lock);
92 }
93
94 static int
95 chn_polltrigger(struct pcm_channel *c)
96 {
97 struct snd_dbuf *bs = c->bufsoft;
98 unsigned amt, lim;
99
100 CHN_LOCKASSERT(c);
101 if (c->flags & CHN_F_MAPPED) {
102 if (sndbuf_getprevblocks(bs) == 0)
103 return 1;
104 else
105 return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
106 } else {
107 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
108 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
109 lim = 1;
110 return (amt >= lim)? 1 : 0;
111 }
112 return 0;
113 }
114
115 static int
116 chn_pollreset(struct pcm_channel *c)
117 {
118 struct snd_dbuf *bs = c->bufsoft;
119
120 CHN_LOCKASSERT(c);
121 sndbuf_updateprevtotal(bs);
122 return 1;
123 }
124
125 static void
126 chn_wakeup(struct pcm_channel *c)
127 {
128 struct snd_dbuf *bs = c->bufsoft;
129 struct pcmchan_children *pce;
130
131 CHN_LOCKASSERT(c);
132 if (SLIST_EMPTY(&c->children)) {
133 if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
134 selwakeuppri(sndbuf_getsel(bs), PRIBIO);
135 } else {
136 SLIST_FOREACH(pce, &c->children, link) {
137 CHN_LOCK(pce->channel);
138 chn_wakeup(pce->channel);
139 CHN_UNLOCK(pce->channel);
140 }
141 }
142
143 wakeup(bs);
144 }
145
146 static int
147 chn_sleep(struct pcm_channel *c, char *str, int timeout)
148 {
149 struct snd_dbuf *bs = c->bufsoft;
150 int ret;
151
152 CHN_LOCKASSERT(c);
153 #ifdef USING_MUTEX
154 ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
155 #else
156 ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
157 #endif
158
159 return ret;
160 }
161
162 /*
163 * chn_dmaupdate() tracks the status of a dma transfer,
164 * updating pointers. It must be called at spltty().
165 */
166
167 static unsigned int
168 chn_dmaupdate(struct pcm_channel *c)
169 {
170 struct snd_dbuf *b = c->bufhard;
171 unsigned int delta, old, hwptr, amt;
172
173 KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
174 CHN_LOCKASSERT(c);
175
176 old = sndbuf_gethwptr(b);
177 hwptr = chn_getptr(c);
178 delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
179 sndbuf_sethwptr(b, hwptr);
180
181 DEB(
182 if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
183 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
184 device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
185 }
186 );
187
188 if (c->direction == PCMDIR_PLAY) {
189 amt = MIN(delta, sndbuf_getready(b));
190 if (amt > 0)
191 sndbuf_dispose(b, NULL, amt);
192 } else {
193 amt = MIN(delta, sndbuf_getfree(b));
194 if (amt > 0)
195 sndbuf_acquire(b, NULL, amt);
196 }
197
198 return delta;
199 }
200
201 void
202 chn_wrupdate(struct pcm_channel *c)
203 {
204 int ret;
205
206 CHN_LOCKASSERT(c);
207 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
208
209 if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
210 return;
211 chn_dmaupdate(c);
212 ret = chn_wrfeed(c);
213 /* tell the driver we've updated the primary buffer */
214 chn_trigger(c, PCMTRIG_EMLDMAWR);
215 DEB(if (ret)
216 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
217
218 }
219
220 int
221 chn_wrfeed(struct pcm_channel *c)
222 {
223 struct snd_dbuf *b = c->bufhard;
224 struct snd_dbuf *bs = c->bufsoft;
225 unsigned int ret, amt;
226
227 CHN_LOCKASSERT(c);
228 /* DEB(
229 if (c->flags & CHN_F_CLOSING) {
230 sndbuf_dump(b, "b", 0x02);
231 sndbuf_dump(bs, "bs", 0x02);
232 }) */
233
234 if (c->flags & CHN_F_MAPPED)
235 sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
236
237 amt = sndbuf_getfree(b);
238 KASSERT(amt <= sndbuf_getsize(bs),
239 ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
240 amt, sndbuf_getsize(bs), c->flags));
241 if (sndbuf_getready(bs) < amt)
242 c->xruns++;
243
244 ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
245 if (ret == 0 && sndbuf_getfree(b) < amt)
246 chn_wakeup(c);
247
248 return ret;
249 }
250
251 static void
252 chn_wrintr(struct pcm_channel *c)
253 {
254 int ret;
255
256 CHN_LOCKASSERT(c);
257 /* update pointers in primary buffer */
258 chn_dmaupdate(c);
259 /* ...and feed from secondary to primary */
260 ret = chn_wrfeed(c);
261 /* tell the driver we've updated the primary buffer */
262 chn_trigger(c, PCMTRIG_EMLDMAWR);
263 DEB(if (ret)
264 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
265 }
266
267 /*
268 * user write routine - uiomove data into secondary buffer, trigger if necessary
269 * if blocking, sleep, rinse and repeat.
270 *
271 * called externally, so must handle locking
272 */
273
274 int
275 chn_write(struct pcm_channel *c, struct uio *buf)
276 {
277 int ret, timeout, newsize, count, sz;
278 struct snd_dbuf *bs = c->bufsoft;
279 void *off;
280 int t, x,togo,p;
281
282 CHN_LOCKASSERT(c);
283 /*
284 * XXX Certain applications attempt to write larger size
285 * of pcm data than c->blocksize2nd without blocking,
286 * resulting partial write. Expand the block size so that
287 * the write operation avoids blocking.
288 */
289 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
290 DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
291 buf->uio_resid, sndbuf_getblksz(bs)));
292 newsize = 16;
293 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
294 newsize <<= 1;
295 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
296 DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
297 }
298
299 ret = 0;
300 count = hz;
301 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
302 sz = sndbuf_getfree(bs);
303 if (sz == 0) {
304 if (c->flags & CHN_F_NBIO)
305 ret = EWOULDBLOCK;
306 else {
307 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
308 if (timeout < 1)
309 timeout = 1;
310 timeout = 1;
311 ret = chn_sleep(c, "pcmwr", timeout);
312 if (ret == EWOULDBLOCK) {
313 count -= timeout;
314 ret = 0;
315 } else if (ret == 0)
316 count = hz;
317 }
318 } else {
319 sz = MIN(sz, buf->uio_resid);
320 KASSERT(sz > 0, ("confusion in chn_write"));
321 /* printf("sz: %d\n", sz); */
322
323 /*
324 * The following assumes that the free space in
325 * the buffer can never be less around the
326 * unlock-uiomove-lock sequence.
327 */
328 togo = sz;
329 while (ret == 0 && togo> 0) {
330 p = sndbuf_getfreeptr(bs);
331 t = MIN(togo, sndbuf_getsize(bs) - p);
332 off = sndbuf_getbufofs(bs, p);
333 CHN_UNLOCK(c);
334 ret = uiomove(off, t, buf);
335 CHN_LOCK(c);
336 togo -= t;
337 x = sndbuf_acquire(bs, NULL, t);
338 }
339 ret = 0;
340 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
341 chn_start(c, 0);
342 }
343 }
344 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
345
346 if (count <= 0) {
347 c->flags |= CHN_F_DEAD;
348 printf("%s: play interrupt timeout, channel dead\n", c->name);
349 }
350
351 return ret;
352 }
353
354 static int
355 chn_rddump(struct pcm_channel *c, unsigned int cnt)
356 {
357 struct snd_dbuf *b = c->bufhard;
358
359 CHN_LOCKASSERT(c);
360 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
361 return sndbuf_dispose(b, NULL, cnt);
362 }
363
364 /*
365 * Feed new data from the read buffer. Can be called in the bottom half.
366 * Hence must be called at spltty.
367 */
368 int
369 chn_rdfeed(struct pcm_channel *c)
370 {
371 struct snd_dbuf *b = c->bufhard;
372 struct snd_dbuf *bs = c->bufsoft;
373 unsigned int ret, amt;
374
375 CHN_LOCKASSERT(c);
376 DEB(
377 if (c->flags & CHN_F_CLOSING) {
378 sndbuf_dump(b, "b", 0x02);
379 sndbuf_dump(bs, "bs", 0x02);
380 })
381
382 amt = sndbuf_getready(b);
383 if (sndbuf_getfree(bs) < amt) {
384 c->xruns++;
385 amt = sndbuf_getfree(bs);
386 }
387 ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
388
389 amt = sndbuf_getready(b);
390 if (amt > 0)
391 chn_rddump(c, amt);
392
393 chn_wakeup(c);
394
395 return ret;
396 }
397
398 void
399 chn_rdupdate(struct pcm_channel *c)
400 {
401 int ret;
402
403 CHN_LOCKASSERT(c);
404 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
405
406 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
407 return;
408 chn_trigger(c, PCMTRIG_EMLDMARD);
409 chn_dmaupdate(c);
410 ret = chn_rdfeed(c);
411 if (ret)
412 printf("chn_rdfeed: %d\n", ret);
413
414 }
415
416 /* read interrupt routine. Must be called with interrupts blocked. */
417 static void
418 chn_rdintr(struct pcm_channel *c)
419 {
420 int ret;
421
422 CHN_LOCKASSERT(c);
423 /* tell the driver to update the primary buffer if non-dma */
424 chn_trigger(c, PCMTRIG_EMLDMARD);
425 /* update pointers in primary buffer */
426 chn_dmaupdate(c);
427 /* ...and feed from primary to secondary */
428 ret = chn_rdfeed(c);
429 }
430
431 /*
432 * user read routine - trigger if necessary, uiomove data from secondary buffer
433 * if blocking, sleep, rinse and repeat.
434 *
435 * called externally, so must handle locking
436 */
437
438 int
439 chn_read(struct pcm_channel *c, struct uio *buf)
440 {
441 int ret, timeout, sz, count;
442 struct snd_dbuf *bs = c->bufsoft;
443 void *off;
444 int t, x,togo,p;
445
446 CHN_LOCKASSERT(c);
447 if (!(c->flags & CHN_F_TRIGGERED))
448 chn_start(c, 0);
449
450 ret = 0;
451 count = hz;
452 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
453 sz = MIN(buf->uio_resid, sndbuf_getready(bs));
454
455 if (sz > 0) {
456 /*
457 * The following assumes that the free space in
458 * the buffer can never be less around the
459 * unlock-uiomove-lock sequence.
460 */
461 togo = sz;
462 while (ret == 0 && togo> 0) {
463 p = sndbuf_getreadyptr(bs);
464 t = MIN(togo, sndbuf_getsize(bs) - p);
465 off = sndbuf_getbufofs(bs, p);
466 CHN_UNLOCK(c);
467 ret = uiomove(off, t, buf);
468 CHN_LOCK(c);
469 togo -= t;
470 x = sndbuf_dispose(bs, NULL, t);
471 }
472 ret = 0;
473 } else {
474 if (c->flags & CHN_F_NBIO) {
475 ret = EWOULDBLOCK;
476 } else {
477 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
478 if (timeout < 1)
479 timeout = 1;
480 ret = chn_sleep(c, "pcmrd", timeout);
481 if (ret == EWOULDBLOCK) {
482 count -= timeout;
483 ret = 0;
484 } else {
485 count = hz;
486 }
487
488 }
489 }
490 }
491
492 if (count <= 0) {
493 c->flags |= CHN_F_DEAD;
494 printf("%s: record interrupt timeout, channel dead\n", c->name);
495 }
496
497 return ret;
498 }
499
500 void
501 chn_intr(struct pcm_channel *c)
502 {
503 CHN_LOCK(c);
504 c->interrupts++;
505 if (c->direction == PCMDIR_PLAY)
506 chn_wrintr(c);
507 else
508 chn_rdintr(c);
509 CHN_UNLOCK(c);
510 }
511
512 u_int32_t
513 chn_start(struct pcm_channel *c, int force)
514 {
515 u_int32_t i, j;
516 struct snd_dbuf *b = c->bufhard;
517 struct snd_dbuf *bs = c->bufsoft;
518
519 CHN_LOCKASSERT(c);
520 /* if we're running, or if we're prevented from triggering, bail */
521 if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
522 return EINVAL;
523
524 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
525 j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
526 if (force || (i >= j)) {
527 c->flags |= CHN_F_TRIGGERED;
528 /*
529 * if we're starting because a vchan started, don't feed any data
530 * or it becomes impossible to start vchans synchronised with the
531 * first one. the hardbuf should be empty so we top it up with
532 * silence to give it something to chew. the real data will be
533 * fed at the first irq.
534 */
535 if (c->direction == PCMDIR_PLAY) {
536 if (SLIST_EMPTY(&c->children))
537 chn_wrfeed(c);
538 else
539 sndbuf_fillsilence(b);
540 }
541 sndbuf_setrun(b, 1);
542 c->xruns = 0;
543 chn_trigger(c, PCMTRIG_START);
544 return 0;
545 }
546
547 return 0;
548 }
549
550 void
551 chn_resetbuf(struct pcm_channel *c)
552 {
553 struct snd_dbuf *b = c->bufhard;
554 struct snd_dbuf *bs = c->bufsoft;
555
556 c->blocks = 0;
557 sndbuf_reset(b);
558 sndbuf_reset(bs);
559 }
560
561 /*
562 * chn_sync waits until the space in the given channel goes above
563 * a threshold. The threshold is checked against fl or rl respectively.
564 * Assume that the condition can become true, do not check here...
565 */
566 int
567 chn_sync(struct pcm_channel *c, int threshold)
568 {
569 u_long rdy;
570 int ret;
571 struct snd_dbuf *bs = c->bufsoft;
572
573 CHN_LOCKASSERT(c);
574
575 /* if we haven't yet started and nothing is buffered, else start*/
576 if (!(c->flags & CHN_F_TRIGGERED)) {
577 if (sndbuf_getready(bs) > 0) {
578 ret = chn_start(c, 1);
579 if (ret)
580 return ret;
581 } else {
582 return 0;
583 }
584 }
585
586 for (;;) {
587 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
588 if (rdy <= threshold) {
589 ret = chn_sleep(c, "pcmsyn", 1);
590 if (ret == ERESTART || ret == EINTR) {
591 DEB(printf("chn_sync: tsleep returns %d\n", ret));
592 return -1;
593 }
594 } else
595 break;
596 }
597 return 0;
598 }
599
600 /* called externally, handle locking */
601 int
602 chn_poll(struct pcm_channel *c, int ev, struct thread *td)
603 {
604 struct snd_dbuf *bs = c->bufsoft;
605 int ret;
606
607 CHN_LOCKASSERT(c);
608 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
609 chn_start(c, 1);
610 ret = 0;
611 if (chn_polltrigger(c) && chn_pollreset(c))
612 ret = ev;
613 else
614 selrecord(td, sndbuf_getsel(bs));
615 return ret;
616 }
617
618 /*
619 * chn_abort terminates a running dma transfer. it may sleep up to 200ms.
620 * it returns the number of bytes that have not been transferred.
621 *
622 * called from: dsp_close, dsp_ioctl, with channel locked
623 */
624 int
625 chn_abort(struct pcm_channel *c)
626 {
627 int missing = 0;
628 struct snd_dbuf *b = c->bufhard;
629 struct snd_dbuf *bs = c->bufsoft;
630
631 CHN_LOCKASSERT(c);
632 if (!(c->flags & CHN_F_TRIGGERED))
633 return 0;
634 c->flags |= CHN_F_ABORTING;
635
636 c->flags &= ~CHN_F_TRIGGERED;
637 /* kill the channel */
638 chn_trigger(c, PCMTRIG_ABORT);
639 sndbuf_setrun(b, 0);
640 if (!(c->flags & CHN_F_VIRTUAL))
641 chn_dmaupdate(c);
642 missing = sndbuf_getready(bs) + sndbuf_getready(b);
643
644 c->flags &= ~CHN_F_ABORTING;
645 return missing;
646 }
647
648 /*
649 * this routine tries to flush the dma transfer. It is called
650 * on a close of a playback channel.
651 * first, if there is data in the buffer, but the dma has not yet
652 * begun, we need to start it.
653 * next, we wait for the play buffer to drain
654 * finally, we stop the dma.
655 *
656 * called from: dsp_close, not valid for record channels.
657 */
658
659 int
660 chn_flush(struct pcm_channel *c)
661 {
662 int ret, count, resid, resid_p;
663 struct snd_dbuf *b = c->bufhard;
664 struct snd_dbuf *bs = c->bufsoft;
665
666 CHN_LOCKASSERT(c);
667 KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
668 DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
669
670 /* if we haven't yet started and nothing is buffered, else start*/
671 if (!(c->flags & CHN_F_TRIGGERED)) {
672 if (sndbuf_getready(bs) > 0) {
673 ret = chn_start(c, 1);
674 if (ret)
675 return ret;
676 } else {
677 return 0;
678 }
679 }
680
681 c->flags |= CHN_F_CLOSING;
682 resid = sndbuf_getready(bs) + sndbuf_getready(b);
683 resid_p = resid;
684 count = 10;
685 ret = 0;
686 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
687 /* still pending output data. */
688 ret = chn_sleep(c, "pcmflu", hz / 10);
689 if (ret == EWOULDBLOCK)
690 ret = 0;
691 if (ret == 0) {
692 resid = sndbuf_getready(bs) + sndbuf_getready(b);
693 if (resid == resid_p)
694 count--;
695 if (resid > resid_p)
696 DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
697 resid_p = resid;
698 }
699 }
700 if (count == 0)
701 DEB(printf("chn_flush: timeout, hw %d, sw %d\n",
702 sndbuf_getready(b), sndbuf_getready(bs)));
703
704 c->flags &= ~CHN_F_TRIGGERED;
705 /* kill the channel */
706 chn_trigger(c, PCMTRIG_ABORT);
707 sndbuf_setrun(b, 0);
708
709 c->flags &= ~CHN_F_CLOSING;
710 return 0;
711 }
712
713 int
714 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
715 {
716 int i;
717
718 for (i = 0; fmtlist[i]; i++)
719 if (fmt == fmtlist[i])
720 return 1;
721 return 0;
722 }
723
724 int
725 chn_reset(struct pcm_channel *c, u_int32_t fmt)
726 {
727 int hwspd, r;
728
729 CHN_LOCKASSERT(c);
730 c->flags &= CHN_F_RESET;
731 c->interrupts = 0;
732 c->xruns = 0;
733
734 r = CHANNEL_RESET(c->methods, c->devinfo);
735 if (fmt != 0) {
736 hwspd = DSP_DEFAULT_SPEED;
737 /* only do this on a record channel until feederbuilder works */
738 if (c->direction == PCMDIR_REC)
739 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
740 c->speed = hwspd;
741
742 if (r == 0)
743 r = chn_setformat(c, fmt);
744 if (r == 0)
745 r = chn_setspeed(c, hwspd);
746 if (r == 0)
747 r = chn_setvolume(c, 100, 100);
748 }
749 if (r == 0)
750 r = chn_setblocksize(c, 0, 0);
751 if (r == 0) {
752 chn_resetbuf(c);
753 r = CHANNEL_RESETDONE(c->methods, c->devinfo);
754 }
755 return r;
756 }
757
758 int
759 chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
760 {
761 struct feeder_class *fc;
762 struct snd_dbuf *b, *bs;
763 int ret;
764
765 chn_lockinit(c, dir);
766
767 b = NULL;
768 bs = NULL;
769 c->devinfo = NULL;
770 c->feeder = NULL;
771
772 ret = ENOMEM;
773 b = sndbuf_create(c->dev, c->name, "primary", c);
774 if (b == NULL)
775 goto out;
776 bs = sndbuf_create(c->dev, c->name, "secondary", c);
777 if (bs == NULL)
778 goto out;
779
780 CHN_LOCK(c);
781
782 ret = EINVAL;
783 fc = feeder_getclass(NULL);
784 if (fc == NULL)
785 goto out;
786 if (chn_addfeeder(c, fc, NULL))
787 goto out;
788
789 /*
790 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
791 * with the channel unlocked because they are also called
792 * from driver methods that don't know about locking
793 */
794 CHN_UNLOCK(c);
795 sndbuf_setup(bs, NULL, 0);
796 CHN_LOCK(c);
797 c->bufhard = b;
798 c->bufsoft = bs;
799 c->flags = 0;
800 c->feederflags = 0;
801
802 ret = ENODEV;
803 CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
804 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
805 CHN_LOCK(c);
806 if (c->devinfo == NULL)
807 goto out;
808
809 ret = ENOMEM;
810 if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
811 goto out;
812
813 ret = chn_setdir(c, direction);
814 if (ret)
815 goto out;
816
817 ret = sndbuf_setfmt(b, AFMT_U8);
818 if (ret)
819 goto out;
820
821 ret = sndbuf_setfmt(bs, AFMT_U8);
822 if (ret)
823 goto out;
824
825
826 out:
827 CHN_UNLOCK(c);
828 if (ret) {
829 if (c->devinfo) {
830 if (CHANNEL_FREE(c->methods, c->devinfo))
831 sndbuf_free(b);
832 }
833 if (bs)
834 sndbuf_destroy(bs);
835 if (b)
836 sndbuf_destroy(b);
837 c->flags |= CHN_F_DEAD;
838 chn_lockdestroy(c);
839
840 return ret;
841 }
842
843 return 0;
844 }
845
846 int
847 chn_kill(struct pcm_channel *c)
848 {
849 struct snd_dbuf *b = c->bufhard;
850 struct snd_dbuf *bs = c->bufsoft;
851
852 if (c->flags & CHN_F_TRIGGERED)
853 chn_trigger(c, PCMTRIG_ABORT);
854 while (chn_removefeeder(c) == 0);
855 if (CHANNEL_FREE(c->methods, c->devinfo))
856 sndbuf_free(b);
857 c->flags |= CHN_F_DEAD;
858 sndbuf_destroy(bs);
859 sndbuf_destroy(b);
860 chn_lockdestroy(c);
861 return 0;
862 }
863
864 int
865 chn_setdir(struct pcm_channel *c, int dir)
866 {
867 struct snd_dbuf *b = c->bufhard;
868 int r;
869
870 CHN_LOCKASSERT(c);
871 c->direction = dir;
872 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
873 if (!r && SND_DMA(b))
874 sndbuf_dmasetdir(b, c->direction);
875 return r;
876 }
877
878 int
879 chn_setvolume(struct pcm_channel *c, int left, int right)
880 {
881 CHN_LOCKASSERT(c);
882 /* should add a feeder for volume changing if channel returns -1 */
883 c->volume = (left << 8) | right;
884 return 0;
885 }
886
887 static int
888 chn_tryspeed(struct pcm_channel *c, int speed)
889 {
890 struct pcm_feeder *f;
891 struct snd_dbuf *b = c->bufhard;
892 struct snd_dbuf *bs = c->bufsoft;
893 struct snd_dbuf *x;
894 int r, delta;
895
896 CHN_LOCKASSERT(c);
897 DEB(printf("setspeed, channel %s\n", c->name));
898 DEB(printf("want speed %d, ", speed));
899 if (speed <= 0)
900 return EINVAL;
901 if (CANCHANGE(c)) {
902 r = 0;
903 c->speed = speed;
904 sndbuf_setspd(bs, speed);
905 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
906 DEB(printf("try speed %d, ", speed));
907 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
908 DEB(printf("got speed %d\n", sndbuf_getspd(b)));
909
910 delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
911 if (delta < 0)
912 delta = -delta;
913
914 c->feederflags &= ~(1 << FEEDER_RATE);
915 if (delta > 500)
916 c->feederflags |= 1 << FEEDER_RATE;
917 else
918 sndbuf_setspd(bs, sndbuf_getspd(b));
919
920 r = chn_buildfeeder(c);
921 DEB(printf("r = %d\n", r));
922 if (r)
923 goto out;
924
925 r = chn_setblocksize(c, 0, 0);
926 if (r)
927 goto out;
928
929 if (!(c->feederflags & (1 << FEEDER_RATE)))
930 goto out;
931
932 r = EINVAL;
933 f = chn_findfeeder(c, FEEDER_RATE);
934 DEB(printf("feedrate = %p\n", f));
935 if (f == NULL)
936 goto out;
937
938 x = (c->direction == PCMDIR_REC)? b : bs;
939 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
940 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
941 if (r)
942 goto out;
943
944 x = (c->direction == PCMDIR_REC)? bs : b;
945 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
946 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
947 out:
948 DEB(printf("setspeed done, r = %d\n", r));
949 return r;
950 } else
951 return EINVAL;
952 }
953
954 int
955 chn_setspeed(struct pcm_channel *c, int speed)
956 {
957 int r, oldspeed = c->speed;
958
959 r = chn_tryspeed(c, speed);
960 if (r) {
961 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
962 r = chn_tryspeed(c, oldspeed);
963 }
964 return r;
965 }
966
967 static int
968 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
969 {
970 struct snd_dbuf *b = c->bufhard;
971 struct snd_dbuf *bs = c->bufsoft;
972 int r;
973
974 CHN_LOCKASSERT(c);
975 if (CANCHANGE(c)) {
976 DEB(printf("want format %d\n", fmt));
977 c->format = fmt;
978 r = chn_buildfeeder(c);
979 if (r == 0) {
980 sndbuf_setfmt(bs, c->format);
981 chn_resetbuf(c);
982 r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
983 if (r == 0)
984 r = chn_tryspeed(c, c->speed);
985 }
986 return r;
987 } else
988 return EINVAL;
989 }
990
991 int
992 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
993 {
994 u_int32_t oldfmt = c->format;
995 int r;
996
997 r = chn_tryformat(c, fmt);
998 if (r) {
999 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
1000 chn_tryformat(c, oldfmt);
1001 }
1002 return r;
1003 }
1004
1005 int
1006 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
1007 {
1008 struct snd_dbuf *b = c->bufhard;
1009 struct snd_dbuf *bs = c->bufsoft;
1010 int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz;
1011
1012 CHN_LOCKASSERT(c);
1013 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
1014 KASSERT(sndbuf_getsize(bs) == 0 ||
1015 sndbuf_getsize(bs) >= sndbuf_getsize(b),
1016 ("%s(%s): bufsoft size %d < bufhard size %d", __func__,
1017 c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
1018 return EINVAL;
1019 }
1020 c->flags |= CHN_F_SETBLOCKSIZE;
1021
1022 ret = 0;
1023 DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
1024 if (blksz == 0 || blksz == -1) {
1025 if (blksz == -1)
1026 c->flags &= ~CHN_F_HAS_SIZE;
1027 if (!(c->flags & CHN_F_HAS_SIZE)) {
1028 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
1029 tmp = 32;
1030 while (tmp <= blksz)
1031 tmp <<= 1;
1032 tmp >>= 1;
1033 blksz = tmp;
1034 blkcnt = CHN_2NDBUFMAXSIZE / blksz;
1035
1036 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
1037 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
1038 DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
1039 } else {
1040 blkcnt = sndbuf_getblkcnt(bs);
1041 blksz = sndbuf_getblksz(bs);
1042 DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
1043 }
1044 } else {
1045 ret = EINVAL;
1046 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
1047 goto out;
1048 ret = 0;
1049 c->flags |= CHN_F_HAS_SIZE;
1050 }
1051
1052 reqblksz = blksz;
1053
1054 /* adjust for different hw format/speed */
1055 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
1056 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
1057 RANGE(irqhz, 16, 512);
1058
1059 tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz;
1060
1061 /* round down to 2^x */
1062 blksz = 32;
1063 while (blksz <= tmpblksz)
1064 blksz <<= 1;
1065 blksz >>= 1;
1066
1067 /* round down to fit hw buffer size */
1068 if (sndbuf_getmaxsize(b) > 0)
1069 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
1070 else
1071 /* virtual channels don't appear to allocate bufhard */
1072 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
1073 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
1074
1075 /* Increase the size of bufsoft if before increasing bufhard. */
1076 maxsize = sndbuf_getsize(b);
1077 if (sndbuf_getsize(bs) > maxsize)
1078 maxsize = sndbuf_getsize(bs);
1079 if (reqblksz * blkcnt > maxsize)
1080 maxsize = reqblksz * blkcnt;
1081 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1082 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1083 if (ret)
1084 goto out1;
1085 }
1086
1087 CHN_UNLOCK(c);
1088 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
1089 CHN_LOCK(c);
1090
1091 /* Decrease the size of bufsoft after decreasing bufhard. */
1092 maxsize = sndbuf_getsize(b);
1093 if (reqblksz * blkcnt > maxsize)
1094 maxsize = reqblksz * blkcnt;
1095 if (maxsize > sndbuf_getsize(bs))
1096 printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
1097 c->name, sndbuf_getsize(bs), maxsize);
1098 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1099 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1100 if (ret)
1101 goto out1;
1102 }
1103
1104 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
1105 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
1106
1107 chn_resetbuf(c);
1108 out1:
1109 KASSERT(sndbuf_getsize(bs) == 0 ||
1110 sndbuf_getsize(bs) >= sndbuf_getsize(b),
1111 ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
1112 __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
1113 blksz, maxsize, blkcnt));
1114 out:
1115 c->flags &= ~CHN_F_SETBLOCKSIZE;
1116 return ret;
1117 }
1118
1119 int
1120 chn_trigger(struct pcm_channel *c, int go)
1121 {
1122 struct snd_dbuf *b = c->bufhard;
1123 int ret;
1124
1125 CHN_LOCKASSERT(c);
1126 if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
1127 sndbuf_dmabounce(b);
1128 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
1129
1130 return ret;
1131 }
1132
1133 int
1134 chn_getptr(struct pcm_channel *c)
1135 {
1136 int hwptr;
1137 int a = (1 << c->align) - 1;
1138
1139 CHN_LOCKASSERT(c);
1140 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1141 /* don't allow unaligned values in the hwa ptr */
1142 #if 1
1143 hwptr &= ~a ; /* Apply channel align mask */
1144 #endif
1145 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1146 return hwptr;
1147 }
1148
1149 struct pcmchan_caps *
1150 chn_getcaps(struct pcm_channel *c)
1151 {
1152 CHN_LOCKASSERT(c);
1153 return CHANNEL_GETCAPS(c->methods, c->devinfo);
1154 }
1155
1156 u_int32_t
1157 chn_getformats(struct pcm_channel *c)
1158 {
1159 u_int32_t *fmtlist, fmts;
1160 int i;
1161
1162 fmtlist = chn_getcaps(c)->fmtlist;
1163 fmts = 0;
1164 for (i = 0; fmtlist[i]; i++)
1165 fmts |= fmtlist[i];
1166
1167 /* report software-supported formats */
1168 if (report_soft_formats)
1169 fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
1170 AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1171
1172 return fmts;
1173 }
1174
1175 static int
1176 chn_buildfeeder(struct pcm_channel *c)
1177 {
1178 struct feeder_class *fc;
1179 struct pcm_feederdesc desc;
1180 u_int32_t tmp[2], type, flags, hwfmt;
1181 int err;
1182
1183 CHN_LOCKASSERT(c);
1184 while (chn_removefeeder(c) == 0);
1185 KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1186
1187 c->align = sndbuf_getalign(c->bufsoft);
1188
1189 if (SLIST_EMPTY(&c->children)) {
1190 fc = feeder_getclass(NULL);
1191 KASSERT(fc != NULL, ("can't find root feeder"));
1192
1193 err = chn_addfeeder(c, fc, NULL);
1194 if (err) {
1195 DEB(printf("can't add root feeder, err %d\n", err));
1196
1197 return err;
1198 }
1199 c->feeder->desc->out = c->format;
1200 } else {
1201 desc.type = FEEDER_MIXER;
1202 desc.in = 0;
1203 desc.out = c->format;
1204 desc.flags = 0;
1205 fc = feeder_getclass(&desc);
1206 if (fc == NULL) {
1207 DEB(printf("can't find vchan feeder\n"));
1208
1209 return EOPNOTSUPP;
1210 }
1211
1212 err = chn_addfeeder(c, fc, &desc);
1213 if (err) {
1214 DEB(printf("can't add vchan feeder, err %d\n", err));
1215
1216 return err;
1217 }
1218 }
1219 flags = c->feederflags;
1220
1221 DEB(printf("feederflags %x\n", flags));
1222
1223 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1224 if (flags & (1 << type)) {
1225 desc.type = type;
1226 desc.in = 0;
1227 desc.out = 0;
1228 desc.flags = 0;
1229 DEB(printf("find feeder type %d, ", type));
1230 fc = feeder_getclass(&desc);
1231 DEB(printf("got %p\n", fc));
1232 if (fc == NULL) {
1233 DEB(printf("can't find required feeder type %d\n", type));
1234
1235 return EOPNOTSUPP;
1236 }
1237
1238 if (c->feeder->desc->out != fc->desc->in) {
1239 DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
1240 tmp[0] = fc->desc->in;
1241 tmp[1] = 0;
1242 if (chn_fmtchain(c, tmp) == 0) {
1243 DEB(printf("failed\n"));
1244
1245 return ENODEV;
1246 }
1247 DEB(printf("ok\n"));
1248 }
1249
1250 err = chn_addfeeder(c, fc, fc->desc);
1251 if (err) {
1252 DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err));
1253
1254 return err;
1255 }
1256 DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out));
1257 }
1258 }
1259
1260 if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
1261 hwfmt = c->feeder->desc->out;
1262 } else {
1263 if (c->direction == PCMDIR_REC) {
1264 tmp[0] = c->format;
1265 tmp[1] = 0;
1266 hwfmt = chn_fmtchain(c, tmp);
1267 } else {
1268 #if 0
1269 u_int32_t *x = chn_getcaps(c)->fmtlist;
1270 printf("acceptable formats for %s:\n", c->name);
1271 while (*x) {
1272 printf("[0x%8x] ", *x);
1273 x++;
1274 }
1275 #endif
1276 hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
1277 }
1278 }
1279
1280 if (hwfmt == 0)
1281 return ENODEV;
1282
1283 sndbuf_setfmt(c->bufhard, hwfmt);
1284
1285 return 0;
1286 }
1287
1288 int
1289 chn_notify(struct pcm_channel *c, u_int32_t flags)
1290 {
1291 struct pcmchan_children *pce;
1292 struct pcm_channel *child;
1293 int run;
1294
1295 CHN_LOCK(c);
1296
1297 if (SLIST_EMPTY(&c->children)) {
1298 CHN_UNLOCK(c);
1299 return ENODEV;
1300 }
1301
1302 run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1303 /*
1304 * if the hwchan is running, we can't change its rate, format or
1305 * blocksize
1306 */
1307 if (run)
1308 flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1309
1310 if (flags & CHN_N_RATE) {
1311 /*
1312 * we could do something here, like scan children and decide on
1313 * the most appropriate rate to mix at, but we don't for now
1314 */
1315 }
1316 if (flags & CHN_N_FORMAT) {
1317 /*
1318 * we could do something here, like scan children and decide on
1319 * the most appropriate mixer feeder to use, but we don't for now
1320 */
1321 }
1322 if (flags & CHN_N_VOLUME) {
1323 /*
1324 * we could do something here but we don't for now
1325 */
1326 }
1327 if (flags & CHN_N_BLOCKSIZE) {
1328 int blksz;
1329 /*
1330 * scan the children, find the lowest blocksize and use that
1331 * for the hard blocksize
1332 */
1333 blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1334 SLIST_FOREACH(pce, &c->children, link) {
1335 child = pce->channel;
1336 CHN_LOCK(child);
1337 if (sndbuf_getblksz(child->bufhard) < blksz)
1338 blksz = sndbuf_getblksz(child->bufhard);
1339 CHN_UNLOCK(child);
1340 }
1341 chn_setblocksize(c, 2, blksz);
1342 }
1343 if (flags & CHN_N_TRIGGER) {
1344 int nrun;
1345 /*
1346 * scan the children, and figure out if any are running
1347 * if so, we need to be running, otherwise we need to be stopped
1348 * if we aren't in our target sstate, move to it
1349 */
1350 nrun = 0;
1351 SLIST_FOREACH(pce, &c->children, link) {
1352 child = pce->channel;
1353 CHN_LOCK(child);
1354 if (child->flags & CHN_F_TRIGGERED)
1355 nrun = 1;
1356 CHN_UNLOCK(child);
1357 }
1358 if (nrun && !run)
1359 chn_start(c, 1);
1360 if (!nrun && run)
1361 chn_abort(c);
1362 }
1363 CHN_UNLOCK(c);
1364 return 0;
1365 }
1366
1367 void
1368 chn_lock(struct pcm_channel *c)
1369 {
1370 CHN_LOCK(c);
1371 }
1372
1373 void
1374 chn_unlock(struct pcm_channel *c)
1375 {
1376 CHN_UNLOCK(c);
1377 }
Cache object: f13acb2dff69f87e1af5376fd18ce928
|