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