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