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