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