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