]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/pcm/dsp.c
This commit was generated by cvs2svn to compensate for changes in r155094,
[FreeBSD/FreeBSD.git] / sys / dev / sound / pcm / dsp.c
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/param.h>
28 #include <sys/queue.h>
29
30 #include <dev/sound/pcm/sound.h>
31
32 SND_DECLARE_FILE("$FreeBSD$");
33
34 #define OLDPCM_IOCTL
35
36 static d_open_t dsp_open;
37 static d_close_t dsp_close;
38 static d_read_t dsp_read;
39 static d_write_t dsp_write;
40 static d_ioctl_t dsp_ioctl;
41 static d_poll_t dsp_poll;
42 static d_mmap_t dsp_mmap;
43
44 struct cdevsw dsp_cdevsw = {
45         .d_version =    D_VERSION,
46         .d_flags =      D_NEEDGIANT,
47         .d_open =       dsp_open,
48         .d_close =      dsp_close,
49         .d_read =       dsp_read,
50         .d_write =      dsp_write,
51         .d_ioctl =      dsp_ioctl,
52         .d_poll =       dsp_poll,
53         .d_mmap =       dsp_mmap,
54         .d_name =       "dsp",
55 };
56
57 #ifdef USING_DEVFS
58 static eventhandler_tag dsp_ehtag;
59 #endif
60
61 static struct snddev_info *
62 dsp_get_info(struct cdev *dev)
63 {
64         struct snddev_info *d;
65         int unit;
66
67         unit = PCMUNIT(dev);
68         if (unit >= devclass_get_maxunit(pcm_devclass))
69                 return NULL;
70         d = devclass_get_softc(pcm_devclass, unit);
71
72         return d;
73 }
74
75 static u_int32_t
76 dsp_get_flags(struct cdev *dev)
77 {
78         device_t bdev;
79         int unit;
80
81         unit = PCMUNIT(dev);
82         if (unit >= devclass_get_maxunit(pcm_devclass))
83                 return 0xffffffff;
84         bdev = devclass_get_device(pcm_devclass, unit);
85
86         return pcm_getflags(bdev);
87 }
88
89 static void
90 dsp_set_flags(struct cdev *dev, u_int32_t flags)
91 {
92         device_t bdev;
93         int unit;
94
95         unit = PCMUNIT(dev);
96         if (unit >= devclass_get_maxunit(pcm_devclass))
97                 return;
98         bdev = devclass_get_device(pcm_devclass, unit);
99
100         pcm_setflags(bdev, flags);
101 }
102
103 /*
104  * return the channels associated with an open device instance.
105  * set the priority if the device is simplex and one direction (only) is
106  * specified.
107  * lock channels specified.
108  */
109 static int
110 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
111 {
112         struct snddev_info *d;
113         u_int32_t flags;
114
115         flags = dsp_get_flags(dev);
116         d = dsp_get_info(dev);
117         pcm_inprog(d, 1);
118         pcm_lock(d);
119         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
120                 ("getchns: read and write both prioritised"));
121
122         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
123                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
124                 dsp_set_flags(dev, flags);
125         }
126
127         *rdch = dev->si_drv1;
128         *wrch = dev->si_drv2;
129         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
130                 if (prio) {
131                         if (*rdch && flags & SD_F_PRIO_WR) {
132                                 dev->si_drv1 = NULL;
133                                 *rdch = pcm_getfakechan(d);
134                         } else if (*wrch && flags & SD_F_PRIO_RD) {
135                                 dev->si_drv2 = NULL;
136                                 *wrch = pcm_getfakechan(d);
137                         }
138                 }
139
140                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
141         }
142         pcm_unlock(d);
143
144         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
145                 CHN_LOCK(*rdch);
146         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
147                 CHN_LOCK(*wrch);
148
149         return 0;
150 }
151
152 /* unlock specified channels */
153 static void
154 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
155 {
156         struct snddev_info *d;
157
158         d = dsp_get_info(dev);
159         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
160                 CHN_UNLOCK(wrch);
161         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
162                 CHN_UNLOCK(rdch);
163         pcm_inprog(d, -1);
164 }
165
166 static int
167 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
168 {
169         struct pcm_channel *rdch, *wrch;
170         struct snddev_info *d;
171         u_int32_t fmt;
172         int devtype;
173         int rdref;
174         int error;
175
176         d = dsp_get_info(i_dev);
177         devtype = PCMDEV(i_dev);
178
179         /* decide default format */
180         switch (devtype) {
181         case SND_DEV_DSP16:
182                 fmt = AFMT_S16_LE;
183                 break;
184
185         case SND_DEV_DSP:
186                 fmt = AFMT_U8;
187                 break;
188
189         case SND_DEV_AUDIO:
190                 fmt = AFMT_MU_LAW;
191                 break;
192
193         case SND_DEV_NORESET:
194                 fmt = 0;
195                 break;
196
197         case SND_DEV_DSPREC:
198                 fmt = AFMT_U8;
199                 if (mode & FWRITE) {
200                         return EINVAL;
201                 }
202                 break;
203
204         default:
205                 panic("impossible devtype %d", devtype);
206         }
207
208         rdref = 0;
209
210         /* lock snddev so nobody else can monkey with it */
211         pcm_lock(d);
212
213         rdch = i_dev->si_drv1;
214         wrch = i_dev->si_drv2;
215
216         if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
217                 /* we're a simplex device and already open, no go */
218                 pcm_unlock(d);
219                 return EBUSY;
220         }
221
222         if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) {
223                 /*
224                  * device already open in one or both directions that
225                  * the opener wants; we can't handle this.
226                  */
227                 pcm_unlock(d);
228                 return EBUSY;
229         }
230
231         /*
232          * if we get here, the open request is valid- either:
233          *   * we were previously not open
234          *   * we were open for play xor record and the opener wants
235          *     the non-open direction
236          */
237         if (flags & FREAD) {
238                 /* open for read */
239                 pcm_unlock(d);
240                 if (devtype == SND_DEV_DSPREC)
241                         rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
242                 else
243                         rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
244                 if (!rdch) {
245                         /* no channel available, exit */
246                         return EBUSY;
247                 }
248                 /* got a channel, already locked for us */
249                 if (chn_reset(rdch, fmt) ||
250                                 (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))) {
251                         pcm_chnrelease(rdch);
252                         pcm_lock(d);
253                         i_dev->si_drv1 = NULL;
254                         pcm_unlock(d);
255                         return ENODEV;
256                 }
257
258                 if (flags & O_NONBLOCK)
259                         rdch->flags |= CHN_F_NBIO;
260                 pcm_chnref(rdch, 1);
261                 CHN_UNLOCK(rdch);
262                 rdref = 1;
263                 /*
264                  * Record channel created, ref'ed and unlocked
265                  */
266                  pcm_lock(d);
267         }
268
269         if (flags & FWRITE) {
270             /* open for write */
271             pcm_unlock(d);
272             wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
273             error = 0;
274
275             if (!wrch)
276                 error = EBUSY; /* XXX Right return code? */
277             else if (chn_reset(wrch, fmt) ||
278                         (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))
279                 error = ENODEV;
280
281             if (error != 0) {
282                 if (wrch) {
283                     /*
284                      * Free play channel
285                      */
286                     pcm_chnrelease(wrch);
287                     pcm_lock(d);
288                     i_dev->si_drv2 = NULL;
289                     pcm_unlock(d);
290                 }
291                 if (rdref) {
292                     /*
293                      * Lock, deref and release previously created record channel
294                      */
295                     CHN_LOCK(rdch);
296                     pcm_chnref(rdch, -1);
297                     pcm_chnrelease(rdch);
298                     pcm_lock(d);
299                     i_dev->si_drv1 = NULL;
300                     pcm_unlock(d);
301                 }
302
303                 return error;
304             }
305
306             if (flags & O_NONBLOCK)
307                 wrch->flags |= CHN_F_NBIO;
308             pcm_chnref(wrch, 1);
309             CHN_UNLOCK(wrch);
310             pcm_lock(d);
311         }
312
313         i_dev->si_drv1 = rdch;
314         i_dev->si_drv2 = wrch;
315
316         pcm_unlock(d);
317         return 0;
318 }
319
320 static int
321 dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
322 {
323         struct pcm_channel *rdch, *wrch;
324         struct snddev_info *d;
325         int refs;
326
327         d = dsp_get_info(i_dev);
328         pcm_lock(d);
329         rdch = i_dev->si_drv1;
330         wrch = i_dev->si_drv2;
331         pcm_unlock(d);
332
333         refs = 0;
334
335         if (rdch) {
336                 CHN_LOCK(rdch);
337                 refs += pcm_chnref(rdch, -1);
338                 CHN_UNLOCK(rdch);
339         }
340         if (wrch) {
341                 CHN_LOCK(wrch);
342                 refs += pcm_chnref(wrch, -1);
343                 CHN_UNLOCK(wrch);
344         }
345
346         /*
347          * If there are no more references, release the channels.
348          */
349         if ((rdch || wrch) && refs == 0) {
350
351                 pcm_lock(d);
352
353                 if (pcm_getfakechan(d))
354                         pcm_getfakechan(d)->flags = 0;
355
356                 i_dev->si_drv1 = NULL;
357                 i_dev->si_drv2 = NULL;
358
359                 dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
360
361                 pcm_unlock(d);
362
363                 if (rdch) {
364                         CHN_LOCK(rdch);
365                         chn_abort(rdch); /* won't sleep */
366                         rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
367                         chn_reset(rdch, 0);
368                         pcm_chnrelease(rdch);
369                 }
370                 if (wrch) {
371                         CHN_LOCK(wrch);
372                         /*
373                          * XXX: Maybe the right behaviour is to abort on non_block.
374                          * It seems that mplayer flushes the audio queue by quickly
375                          * closing and re-opening.  In FBSD, there's a long pause
376                          * while the audio queue flushes that I presume isn't there in
377                          * linux.
378                          */
379                         chn_flush(wrch); /* may sleep */
380                         wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
381                         chn_reset(wrch, 0);
382                         pcm_chnrelease(wrch);
383                 }
384         }
385         return 0;
386 }
387
388 static int
389 dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
390 {
391         struct pcm_channel *rdch, *wrch;
392         int ret;
393
394         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
395
396         KASSERT(rdch, ("dsp_read: nonexistant channel"));
397         KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
398
399         if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
400                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
401                 return EINVAL;
402         }
403         if (!(rdch->flags & CHN_F_RUNNING))
404                 rdch->flags |= CHN_F_RUNNING;
405         ret = chn_read(rdch, buf);
406         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
407
408         return ret;
409 }
410
411 static int
412 dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
413 {
414         struct pcm_channel *rdch, *wrch;
415         int ret;
416
417         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
418
419         KASSERT(wrch, ("dsp_write: nonexistant channel"));
420         KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
421
422         if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
423                 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
424                 return EINVAL;
425         }
426         if (!(wrch->flags & CHN_F_RUNNING))
427                 wrch->flags |= CHN_F_RUNNING;
428         ret = chn_write(wrch, buf);
429         relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
430
431         return ret;
432 }
433
434 static int
435 dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
436 {
437         struct pcm_channel *chn, *rdch, *wrch;
438         struct snddev_info *d;
439         int kill;
440         int ret = 0, *arg_i = (int *)arg, tmp;
441
442         /*
443          * this is an evil hack to allow broken apps to perform mixer ioctls
444          * on dsp devices.
445          */
446
447         d = dsp_get_info(i_dev);
448         if (IOCGROUP(cmd) == 'M')
449                 return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td);
450
451         getchns(i_dev, &rdch, &wrch, 0);
452
453         kill = 0;
454         if (wrch && (wrch->flags & CHN_F_DEAD))
455                 kill |= 1;
456         if (rdch && (rdch->flags & CHN_F_DEAD))
457                 kill |= 2;
458         if (kill == 3) {
459                 relchns(i_dev, rdch, wrch, 0);
460                 return EINVAL;
461         }
462         if (kill & 1)
463                 wrch = NULL;
464         if (kill & 2)
465                 rdch = NULL;
466         
467         switch(cmd) {
468 #ifdef OLDPCM_IOCTL
469         /*
470          * we start with the new ioctl interface.
471          */
472         case AIONWRITE: /* how many bytes can write ? */
473                 if (wrch) {
474                         CHN_LOCK(wrch);
475 /*
476                 if (wrch && wrch->bufhard.dl)
477                         while (chn_wrfeed(wrch) == 0);
478 */
479                         *arg_i = sndbuf_getfree(wrch->bufsoft);
480                         CHN_UNLOCK(wrch);
481                 } else {
482                         *arg_i = 0;
483                         ret = EINVAL;
484                 }
485                 break;
486
487         case AIOSSIZE:     /* set the current blocksize */
488                 {
489                         struct snd_size *p = (struct snd_size *)arg;
490
491                         p->play_size = 0;
492                         p->rec_size = 0;
493                         if (wrch) {
494                                 CHN_LOCK(wrch);
495                                 chn_setblocksize(wrch, 2, p->play_size);
496                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
497                                 CHN_UNLOCK(wrch);
498                         }
499                         if (rdch) {
500                                 CHN_LOCK(rdch);
501                                 chn_setblocksize(rdch, 2, p->rec_size);
502                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
503                                 CHN_UNLOCK(rdch);
504                         }
505                 }
506                 break;
507         case AIOGSIZE:  /* get the current blocksize */
508                 {
509                         struct snd_size *p = (struct snd_size *)arg;
510
511                         if (wrch) {
512                                 CHN_LOCK(wrch);
513                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
514                                 CHN_UNLOCK(wrch);
515                         }
516                         if (rdch) {
517                                 CHN_LOCK(rdch);
518                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
519                                 CHN_UNLOCK(rdch);
520                         }
521                 }
522                 break;
523
524         case AIOSFMT:
525         case AIOGFMT:
526                 {
527                         snd_chan_param *p = (snd_chan_param *)arg;
528
529                         if (cmd == AIOSFMT &&
530                             ((p->play_format != 0 && p->play_rate == 0) ||
531                             (p->rec_format != 0 && p->rec_rate == 0))) {
532                                 ret = EINVAL;
533                                 break;
534                         }
535                         if (wrch) {
536                                 CHN_LOCK(wrch);
537                                 if (cmd == AIOSFMT && p->play_format != 0) {
538                                         chn_setformat(wrch, p->play_format);
539                                         chn_setspeed(wrch, p->play_rate);
540                                 }
541                                 p->play_rate = wrch->speed;
542                                 p->play_format = wrch->format;
543                                 CHN_UNLOCK(wrch);
544                         } else {
545                                 p->play_rate = 0;
546                                 p->play_format = 0;
547                         }
548                         if (rdch) {
549                                 CHN_LOCK(rdch);
550                                 if (cmd == AIOSFMT && p->rec_format != 0) {
551                                         chn_setformat(rdch, p->rec_format);
552                                         chn_setspeed(rdch, p->rec_rate);
553                                 }
554                                 p->rec_rate = rdch->speed;
555                                 p->rec_format = rdch->format;
556                                 CHN_UNLOCK(rdch);
557                         } else {
558                                 p->rec_rate = 0;
559                                 p->rec_format = 0;
560                         }
561                 }
562                 break;
563
564         case AIOGCAP:     /* get capabilities */
565                 {
566                         snd_capabilities *p = (snd_capabilities *)arg;
567                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
568                         struct cdev *pdev;
569
570                         if (rdch) {
571                                 CHN_LOCK(rdch);
572                                 rcaps = chn_getcaps(rdch);
573                         }
574                         if (wrch) {
575                                 CHN_LOCK(wrch);
576                                 pcaps = chn_getcaps(wrch);
577                         }
578                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
579                                           pcaps? pcaps->minspeed : 0);
580                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
581                                           pcaps? pcaps->maxspeed : 1000000);
582                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
583                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
584                         /* XXX bad on sb16 */
585                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
586                                      (wrch? chn_getformats(wrch) : 0xffffffff);
587                         if (rdch && wrch)
588                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
589                         pdev = d->mixer_dev;
590                         p->mixers = 1; /* default: one mixer */
591                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
592                         p->left = p->right = 100;
593                         if (rdch)
594                                 CHN_UNLOCK(rdch);
595                         if (wrch)
596                                 CHN_UNLOCK(wrch);
597                 }
598                 break;
599
600         case AIOSTOP:
601                 if (*arg_i == AIOSYNC_PLAY && wrch) {
602                         CHN_LOCK(wrch);
603                         *arg_i = chn_abort(wrch);
604                         CHN_UNLOCK(wrch);
605                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
606                         CHN_LOCK(rdch);
607                         *arg_i = chn_abort(rdch);
608                         CHN_UNLOCK(rdch);
609                 } else {
610                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
611                         *arg_i = 0;
612                 }
613                 break;
614
615         case AIOSYNC:
616                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
617                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
618                 break;
619 #endif
620         /*
621          * here follow the standard ioctls (filio.h etc.)
622          */
623         case FIONREAD: /* get # bytes to read */
624                 if (rdch) {
625                         CHN_LOCK(rdch);
626 /*                      if (rdch && rdch->bufhard.dl)
627                                 while (chn_rdfeed(rdch) == 0);
628 */
629                         *arg_i = sndbuf_getready(rdch->bufsoft);
630                         CHN_UNLOCK(rdch);
631                 } else {
632                         *arg_i = 0;
633                         ret = EINVAL;
634                 }
635                 break;
636
637         case FIOASYNC: /*set/clear async i/o */
638                 DEB( printf("FIOASYNC\n") ; )
639                 break;
640
641         case SNDCTL_DSP_NONBLOCK:
642         case FIONBIO: /* set/clear non-blocking i/o */
643                 if (rdch) {
644                         CHN_LOCK(rdch);
645                         if (*arg_i)
646                                 rdch->flags |= CHN_F_NBIO;
647                         else
648                                 rdch->flags &= ~CHN_F_NBIO;
649                         CHN_UNLOCK(rdch);
650                 }
651                 if (wrch) {
652                         CHN_LOCK(wrch);
653                         if (*arg_i)
654                                 wrch->flags |= CHN_F_NBIO;
655                         else
656                                 wrch->flags &= ~CHN_F_NBIO;
657                         CHN_UNLOCK(wrch);
658                 }
659                 break;
660
661         /*
662          * Finally, here is the linux-compatible ioctl interface
663          */
664 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
665         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
666         case SNDCTL_DSP_GETBLKSIZE:
667                 chn = wrch ? wrch : rdch;
668                 if (chn) {
669                         CHN_LOCK(chn);
670                         *arg_i = sndbuf_getblksz(chn->bufsoft);
671                         CHN_UNLOCK(chn);
672                 } else {
673                         *arg_i = 0;
674                         ret = EINVAL;
675                 }
676                 break ;
677
678         case SNDCTL_DSP_SETBLKSIZE:
679                 RANGE(*arg_i, 16, 65536);
680                 if (wrch) {
681                         CHN_LOCK(wrch);
682                         chn_setblocksize(wrch, 2, *arg_i);
683                         CHN_UNLOCK(wrch);
684                 }
685                 if (rdch) {
686                         CHN_LOCK(rdch);
687                         chn_setblocksize(rdch, 2, *arg_i);
688                         CHN_UNLOCK(rdch);
689                 }
690                 break;
691
692         case SNDCTL_DSP_RESET:
693                 DEB(printf("dsp reset\n"));
694                 if (wrch) {
695                         CHN_LOCK(wrch);
696                         chn_abort(wrch);
697                         chn_resetbuf(wrch);
698                         CHN_UNLOCK(wrch);
699                 }
700                 if (rdch) {
701                         CHN_LOCK(rdch);
702                         chn_abort(rdch);
703                         chn_resetbuf(rdch);
704                         CHN_UNLOCK(rdch);
705                 }
706                 break;
707
708         case SNDCTL_DSP_SYNC:
709                 DEB(printf("dsp sync\n"));
710                 /* chn_sync may sleep */
711                 if (wrch) {
712                         CHN_LOCK(wrch);
713                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
714                         CHN_UNLOCK(wrch);
715                 }
716                 break;
717
718         case SNDCTL_DSP_SPEED:
719                 /* chn_setspeed may sleep */
720                 tmp = 0;
721                 if (wrch) {
722                         CHN_LOCK(wrch);
723                         ret = chn_setspeed(wrch, *arg_i);
724                         tmp = wrch->speed;
725                         CHN_UNLOCK(wrch);
726                 }
727                 if (rdch && ret == 0) {
728                         CHN_LOCK(rdch);
729                         ret = chn_setspeed(rdch, *arg_i);
730                         if (tmp == 0)
731                                 tmp = rdch->speed;
732                         CHN_UNLOCK(rdch);
733                 }
734                 *arg_i = tmp;
735                 break;
736
737         case SOUND_PCM_READ_RATE:
738                 chn = wrch ? wrch : rdch;
739                 if (chn) {
740                         CHN_LOCK(chn);
741                         *arg_i = chn->speed;
742                         CHN_UNLOCK(chn);
743                 } else {
744                         *arg_i = 0;
745                         ret = EINVAL;
746                 }
747                 break;
748
749         case SNDCTL_DSP_STEREO:
750                 tmp = -1;
751                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
752                 if (wrch) {
753                         CHN_LOCK(wrch);
754                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
755                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
756                         CHN_UNLOCK(wrch);
757                 }
758                 if (rdch && ret == 0) {
759                         CHN_LOCK(rdch);
760                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
761                         if (tmp == -1)
762                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
763                         CHN_UNLOCK(rdch);
764                 }
765                 *arg_i = tmp;
766                 break;
767
768         case SOUND_PCM_WRITE_CHANNELS:
769 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
770                 if (*arg_i != 0) {
771                         tmp = 0;
772                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
773                         if (wrch) {
774                                 CHN_LOCK(wrch);
775                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
776                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
777                                 CHN_UNLOCK(wrch);
778                         }
779                         if (rdch && ret == 0) {
780                                 CHN_LOCK(rdch);
781                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
782                                 if (tmp == 0)
783                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
784                                 CHN_UNLOCK(rdch);
785                         }
786                         *arg_i = tmp;
787                 } else {
788                         chn = wrch ? wrch : rdch;
789                         CHN_LOCK(chn);
790                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
791                         CHN_UNLOCK(chn);
792                 }
793                 break;
794
795         case SOUND_PCM_READ_CHANNELS:
796                 chn = wrch ? wrch : rdch;
797                 if (chn) {
798                         CHN_LOCK(chn);
799                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
800                         CHN_UNLOCK(chn);
801                 } else {
802                         *arg_i = 0;
803                         ret = EINVAL;
804                 }
805                 break;
806
807         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
808                 chn = wrch ? wrch : rdch;
809                 if (chn) {
810                         CHN_LOCK(chn);
811                         *arg_i = chn_getformats(chn);
812                         CHN_UNLOCK(chn);
813                 } else {
814                         *arg_i = 0;
815                         ret = EINVAL;
816                 }
817                 break ;
818
819         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
820                 if ((*arg_i != AFMT_QUERY)) {
821                         tmp = 0;
822                         if (wrch) {
823                                 CHN_LOCK(wrch);
824                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
825                                 tmp = wrch->format & ~AFMT_STEREO;
826                                 CHN_UNLOCK(wrch);
827                         }
828                         if (rdch && ret == 0) {
829                                 CHN_LOCK(rdch);
830                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
831                                 if (tmp == 0)
832                                         tmp = rdch->format & ~AFMT_STEREO;
833                                 CHN_UNLOCK(rdch);
834                         }
835                         *arg_i = tmp;
836                 } else {
837                         chn = wrch ? wrch : rdch;
838                         CHN_LOCK(chn);
839                         *arg_i = chn->format & ~AFMT_STEREO;
840                         CHN_UNLOCK(chn);
841                 }
842                 break;
843
844         case SNDCTL_DSP_SETFRAGMENT:
845                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
846                 {
847                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
848                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
849                         u_int32_t fragsz;
850                         u_int32_t r_maxfrags, r_fragsz;
851
852                         RANGE(fragln, 4, 16);
853                         fragsz = 1 << fragln;
854
855                         if (maxfrags == 0)
856                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
857                         if (maxfrags < 2)
858                                 maxfrags = 2;
859                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
860                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
861
862                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
863                         if (rdch) {
864                                 CHN_LOCK(rdch);
865                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
866                                 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
867                                 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
868                                 CHN_UNLOCK(rdch);
869                         } else {
870                                 r_maxfrags = maxfrags;
871                                 r_fragsz = fragsz;
872                         }
873                         if (wrch && ret == 0) {
874                                 CHN_LOCK(wrch);
875                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
876                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
877                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
878                                 CHN_UNLOCK(wrch);
879                         } else { /* use whatever came from the read channel */
880                                 maxfrags = r_maxfrags;
881                                 fragsz = r_fragsz;
882                         }
883
884                         fragln = 0;
885                         while (fragsz > 1) {
886                                 fragln++;
887                                 fragsz >>= 1;
888                         }
889                         *arg_i = (maxfrags << 16) | fragln;
890                 }
891                 break;
892
893         case SNDCTL_DSP_GETISPACE:
894                 /* return the size of data available in the input queue */
895                 {
896                         audio_buf_info *a = (audio_buf_info *)arg;
897                         if (rdch) {
898                                 struct snd_dbuf *bs = rdch->bufsoft;
899
900                                 CHN_LOCK(rdch);
901                                 a->bytes = sndbuf_getready(bs);
902                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
903                                 a->fragstotal = sndbuf_getblkcnt(bs);
904                                 a->fragsize = sndbuf_getblksz(bs);
905                                 CHN_UNLOCK(rdch);
906                         }
907                 }
908                 break;
909
910         case SNDCTL_DSP_GETOSPACE:
911                 /* return space available in the output queue */
912                 {
913                         audio_buf_info *a = (audio_buf_info *)arg;
914                         if (wrch) {
915                                 struct snd_dbuf *bs = wrch->bufsoft;
916
917                                 CHN_LOCK(wrch);
918                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
919                                 a->bytes = sndbuf_getfree(bs);
920                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
921                                 a->fragstotal = sndbuf_getblkcnt(bs);
922                                 a->fragsize = sndbuf_getblksz(bs);
923                                 CHN_UNLOCK(wrch);
924                         }
925                 }
926                 break;
927
928         case SNDCTL_DSP_GETIPTR:
929                 {
930                         count_info *a = (count_info *)arg;
931                         if (rdch) {
932                                 struct snd_dbuf *bs = rdch->bufsoft;
933
934                                 CHN_LOCK(rdch);
935                                 /* XXX abusive DMA update: chn_rdupdate(rdch); */
936                                 a->bytes = sndbuf_gettotal(bs);
937                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
938                                 a->ptr = sndbuf_getreadyptr(bs);
939                                 rdch->blocks = sndbuf_getblocks(bs);
940                                 CHN_UNLOCK(rdch);
941                         } else
942                                 ret = EINVAL;
943                 }
944                 break;
945
946         case SNDCTL_DSP_GETOPTR:
947                 {
948                         count_info *a = (count_info *)arg;
949                         if (wrch) {
950                                 struct snd_dbuf *bs = wrch->bufsoft;
951
952                                 CHN_LOCK(wrch);
953                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
954                                 a->bytes = sndbuf_gettotal(bs);
955                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
956                                 a->ptr = sndbuf_getreadyptr(bs);
957                                 wrch->blocks = sndbuf_getblocks(bs);
958                                 CHN_UNLOCK(wrch);
959                         } else
960                                 ret = EINVAL;
961                 }
962                 break;
963
964         case SNDCTL_DSP_GETCAPS:
965                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
966                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
967                         *arg_i |= DSP_CAP_DUPLEX;
968                 break;
969
970         case SOUND_PCM_READ_BITS:
971                 chn = wrch ? wrch : rdch;
972                 if (chn) {
973                         CHN_LOCK(chn);
974                         if (chn->format & AFMT_8BIT)
975                                 *arg_i = 8;
976                         else if (chn->format & AFMT_16BIT)
977                                 *arg_i = 16;
978                         else if (chn->format & AFMT_24BIT)
979                                 *arg_i = 24;
980                         else if (chn->format & AFMT_32BIT)
981                                 *arg_i = 32;
982                         else
983                                 ret = EINVAL;
984                         CHN_UNLOCK(chn);
985                 } else {
986                         *arg_i = 0;
987                         ret = EINVAL;
988                 }
989                 break;
990
991         case SNDCTL_DSP_SETTRIGGER:
992                 if (rdch) {
993                         CHN_LOCK(rdch);
994                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
995                         if (*arg_i & PCM_ENABLE_INPUT)
996                                 chn_start(rdch, 1);
997                         else
998                                 rdch->flags |= CHN_F_NOTRIGGER;
999                         CHN_UNLOCK(rdch);
1000                 }
1001                 if (wrch) {
1002                         CHN_LOCK(wrch);
1003                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1004                         if (*arg_i & PCM_ENABLE_OUTPUT)
1005                                 chn_start(wrch, 1);
1006                         else
1007                                 wrch->flags |= CHN_F_NOTRIGGER;
1008                         CHN_UNLOCK(wrch);
1009                 }
1010                 break;
1011
1012         case SNDCTL_DSP_GETTRIGGER:
1013                 *arg_i = 0;
1014                 if (wrch) {
1015                         CHN_LOCK(wrch);
1016                         if (wrch->flags & CHN_F_TRIGGERED)
1017                                 *arg_i |= PCM_ENABLE_OUTPUT;
1018                         CHN_UNLOCK(wrch);
1019                 }
1020                 if (rdch) {
1021                         CHN_LOCK(rdch);
1022                         if (rdch->flags & CHN_F_TRIGGERED)
1023                                 *arg_i |= PCM_ENABLE_INPUT;
1024                         CHN_UNLOCK(rdch);
1025                 }
1026                 break;
1027
1028         case SNDCTL_DSP_GETODELAY:
1029                 if (wrch) {
1030                         struct snd_dbuf *b = wrch->bufhard;
1031                         struct snd_dbuf *bs = wrch->bufsoft;
1032
1033                         CHN_LOCK(wrch);
1034                         /* XXX abusive DMA update: chn_wrupdate(wrch); */
1035                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
1036                         CHN_UNLOCK(wrch);
1037                 } else
1038                         ret = EINVAL;
1039                 break;
1040
1041         case SNDCTL_DSP_POST:
1042                 if (wrch) {
1043                         CHN_LOCK(wrch);
1044                         wrch->flags &= ~CHN_F_NOTRIGGER;
1045                         chn_start(wrch, 1);
1046                         CHN_UNLOCK(wrch);
1047                 }
1048                 break;
1049
1050         case SNDCTL_DSP_SETDUPLEX:
1051                 /*
1052                  * switch to full-duplex mode if card is in half-duplex
1053                  * mode and is able to work in full-duplex mode
1054                  */
1055                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1056                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
1057                 break;
1058
1059         case SNDCTL_DSP_MAPINBUF:
1060         case SNDCTL_DSP_MAPOUTBUF:
1061         case SNDCTL_DSP_SETSYNCRO:
1062                 /* undocumented */
1063
1064         case SNDCTL_DSP_SUBDIVIDE:
1065         case SOUND_PCM_WRITE_FILTER:
1066         case SOUND_PCM_READ_FILTER:
1067                 /* dunno what these do, don't sound important */
1068
1069         default:
1070                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
1071                 ret = EINVAL;
1072                 break;
1073         }
1074         relchns(i_dev, rdch, wrch, 0);
1075         return ret;
1076 }
1077
1078 static int
1079 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
1080 {
1081         struct pcm_channel *wrch = NULL, *rdch = NULL;
1082         int ret, e;
1083
1084         ret = 0;
1085         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1086
1087         if (wrch) {
1088                 e = (events & (POLLOUT | POLLWRNORM));
1089                 if (e)
1090                         ret |= chn_poll(wrch, e, td);
1091         }
1092         if (rdch) {
1093                 e = (events & (POLLIN | POLLRDNORM));
1094                 if (e)
1095                         ret |= chn_poll(rdch, e, td);
1096         }
1097         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1098
1099         return ret;
1100 }
1101
1102 static int
1103 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
1104 {
1105         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1106
1107         if (nprot & PROT_EXEC)
1108                 return -1;
1109
1110         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1111 #if 0
1112         /*
1113          * XXX the linux api uses the nprot to select read/write buffer
1114          * our vm system doesn't allow this, so force write buffer
1115          */
1116
1117         if (wrch && (nprot & PROT_WRITE)) {
1118                 c = wrch;
1119         } else if (rdch && (nprot & PROT_READ)) {
1120                 c = rdch;
1121         } else {
1122                 return -1;
1123         }
1124 #else
1125         c = wrch;
1126 #endif
1127
1128         if (c == NULL) {
1129                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1130                 return -1;
1131         }
1132
1133         if (offset >= sndbuf_getsize(c->bufsoft)) {
1134                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1135                 return -1;
1136         }
1137
1138         if (!(c->flags & CHN_F_MAPPED))
1139                 c->flags |= CHN_F_MAPPED;
1140
1141         *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
1142         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1143
1144         return 0;
1145 }
1146
1147 #ifdef USING_DEVFS
1148
1149 /*
1150  * Clone logic is this:
1151  * x E X = {dsp, dspW, audio}
1152  * x -> x${sysctl("hw.snd.unit")}
1153  * xN->
1154  *    for i N = 1 to channels of device N
1155  *      if xN.i isn't busy, return its dev_t
1156  */
1157 static void
1158 dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
1159     struct cdev **dev)
1160 {
1161         struct cdev *pdev;
1162         struct snddev_info *pcm_dev;
1163         struct snddev_channel *pcm_chan;
1164         int i, unit, devtype;
1165         int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1166         char *devnames[3] = {"dsp", "dspW", "audio"};
1167
1168         if (*dev != NULL)
1169                 return;
1170         if (pcm_devclass == NULL)
1171                 return;
1172
1173         devtype = 0;
1174         unit = -1;
1175         for (i = 0; (i < 3) && (unit == -1); i++) {
1176                 devtype = devtypes[i];
1177                 if (strcmp(name, devnames[i]) == 0) {
1178                         unit = snd_unit;
1179                 } else {
1180                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1181                                 unit = -1;
1182                 }
1183         }
1184         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1185                 return;
1186
1187         pcm_dev = devclass_get_softc(pcm_devclass, unit);
1188
1189         if (pcm_dev == NULL)
1190                 return;
1191
1192         SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
1193
1194                 switch(devtype) {
1195                         case SND_DEV_DSP:
1196                                 pdev = pcm_chan->dsp_devt;
1197                                 break;
1198                         case SND_DEV_DSP16:
1199                                 pdev = pcm_chan->dspW_devt;
1200                                 break;
1201                         case SND_DEV_AUDIO:
1202                                 pdev = pcm_chan->audio_devt;
1203                                 break;
1204                         default:
1205                                 panic("Unknown devtype %d", devtype);
1206                 }
1207
1208                 if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1209                         *dev = pdev;
1210                         dev_ref(*dev);
1211                         return;
1212                 }
1213         }
1214 }
1215
1216 static void
1217 dsp_sysinit(void *p)
1218 {
1219         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1220 }
1221
1222 static void
1223 dsp_sysuninit(void *p)
1224 {
1225         if (dsp_ehtag != NULL)
1226                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1227 }
1228
1229 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1230 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1231 #endif
1232
1233