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