Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / sound / pcm / channel.c
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: src/sys/dev/sound/pcm/channel.c,v 1.19.2.19 2003/03/11 15:15:41 orion Exp $");
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 }