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