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