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