Remove ASR_MEASURE_PERFORMANCE, it doesn't work anyway.
[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.7 2004/05/21 01:14:27 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.7 2004/05/21 01:14:27 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         /* clone */     NULL,
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 = make_adhoc_dev(&dsp_cdevsw, 
457                                 PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
458                 return mixer_ioctl(pdev, cmd, arg, mode, td);
459         }
460
461         s = spltty();
462         d = dsp_get_info(i_dev);
463         getchns(i_dev, &rdch, &wrch, 0);
464
465         kill = 0;
466         if (wrch && (wrch->flags & CHN_F_DEAD))
467                 kill |= 1;
468         if (rdch && (rdch->flags & CHN_F_DEAD))
469                 kill |= 2;
470         if (kill == 3) {
471                 relchns(i_dev, rdch, wrch, 0);
472                 splx(s);
473                 return EINVAL;
474         }
475         if (kill & 1)
476                 wrch = NULL;
477         if (kill & 2)
478                 rdch = NULL;
479
480         switch(cmd) {
481 #ifdef OLDPCM_IOCTL
482         /*
483          * we start with the new ioctl interface.
484          */
485         case AIONWRITE: /* how many bytes can write ? */
486 /*
487                 if (wrch && wrch->bufhard.dl)
488                         while (chn_wrfeed(wrch) == 0);
489 */
490                 *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
491                 break;
492
493         case AIOSSIZE:     /* set the current blocksize */
494                 {
495                         struct snd_size *p = (struct snd_size *)arg;
496
497                         p->play_size = 0;
498                         p->rec_size = 0;
499                         if (wrch) {
500                                 CHN_LOCK(wrch);
501                                 chn_setblocksize(wrch, 2, p->play_size);
502                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
503                                 CHN_UNLOCK(wrch);
504                         }
505                         if (rdch) {
506                                 CHN_LOCK(rdch);
507                                 chn_setblocksize(rdch, 2, p->rec_size);
508                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
509                                 CHN_UNLOCK(rdch);
510                         }
511                 }
512                 break;
513         case AIOGSIZE:  /* get the current blocksize */
514                 {
515                         struct snd_size *p = (struct snd_size *)arg;
516
517                         if (wrch)
518                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
519                         if (rdch)
520                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
521                 }
522                 break;
523
524         case AIOSFMT:
525                 {
526                         snd_chan_param *p = (snd_chan_param *)arg;
527
528                         if (wrch) {
529                                 CHN_LOCK(wrch);
530                                 chn_setformat(wrch, p->play_format);
531                                 chn_setspeed(wrch, p->play_rate);
532                                 CHN_UNLOCK(wrch);
533                         }
534                         if (rdch) {
535                                 CHN_LOCK(rdch);
536                                 chn_setformat(rdch, p->rec_format);
537                                 chn_setspeed(rdch, p->rec_rate);
538                                 CHN_UNLOCK(rdch);
539                         }
540                 }
541                 /* FALLTHROUGH */
542
543         case AIOGFMT:
544                 {
545                         snd_chan_param *p = (snd_chan_param *)arg;
546
547                         p->play_rate = wrch? wrch->speed : 0;
548                         p->rec_rate = rdch? rdch->speed : 0;
549                         p->play_format = wrch? wrch->format : 0;
550                         p->rec_format = rdch? rdch->format : 0;
551                 }
552                 break;
553
554         case AIOGCAP:     /* get capabilities */
555                 {
556                         snd_capabilities *p = (snd_capabilities *)arg;
557                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
558                         dev_t pdev;
559
560                         if (rdch) {
561                                 CHN_LOCK(rdch);
562                                 rcaps = chn_getcaps(rdch);
563                         }
564                         if (wrch) {
565                                 CHN_LOCK(wrch);
566                                 pcaps = chn_getcaps(wrch);
567                         }
568                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
569                                           pcaps? pcaps->minspeed : 0);
570                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
571                                           pcaps? pcaps->maxspeed : 1000000);
572                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
573                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
574                         /* XXX bad on sb16 */
575                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
576                                      (wrch? chn_getformats(wrch) : 0xffffffff);
577                         if (rdch && wrch)
578                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
579                         pdev = make_adhoc_dev(&dsp_cdevsw, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
580                         p->mixers = 1; /* default: one mixer */
581                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
582                         p->left = p->right = 100;
583                         if (wrch)
584                                 CHN_UNLOCK(wrch);
585                         if (rdch)
586                                 CHN_UNLOCK(rdch);
587                 }
588                 break;
589
590         case AIOSTOP:
591                 if (*arg_i == AIOSYNC_PLAY && wrch)
592                         *arg_i = chn_abort(wrch);
593                 else if (*arg_i == AIOSYNC_CAPTURE && rdch)
594                         *arg_i = chn_abort(rdch);
595                 else {
596                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
597                         *arg_i = 0;
598                 }
599                 break;
600
601         case AIOSYNC:
602                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
603                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
604                 break;
605 #endif
606         /*
607          * here follow the standard ioctls (filio.h etc.)
608          */
609         case FIONREAD: /* get # bytes to read */
610 /*              if (rdch && rdch->bufhard.dl)
611                         while (chn_rdfeed(rdch) == 0);
612 */              *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0;
613                 break;
614
615         case FIOASYNC: /*set/clear async i/o */
616                 DEB( printf("FIOASYNC\n") ; )
617                 break;
618
619         case SNDCTL_DSP_NONBLOCK:
620         case FIONBIO: /* set/clear non-blocking i/o */
621                 if (rdch)
622                         rdch->flags &= ~CHN_F_NBIO;
623                 if (wrch)
624                         wrch->flags &= ~CHN_F_NBIO;
625                 if (*arg_i) {
626                         if (rdch)
627                                 rdch->flags |= CHN_F_NBIO;
628                         if (wrch)
629                                 wrch->flags |= CHN_F_NBIO;
630                 }
631                 break;
632
633         /*
634          * Finally, here is the linux-compatible ioctl interface
635          */
636 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
637         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
638         case SNDCTL_DSP_GETBLKSIZE:
639                 if (wrch)
640                         *arg_i = sndbuf_getblksz(wrch->bufsoft);
641                 else if (rdch)
642                         *arg_i = sndbuf_getblksz(rdch->bufsoft);
643                 else
644                         *arg_i = 0;
645                 break ;
646
647         case SNDCTL_DSP_SETBLKSIZE:
648                 RANGE(*arg_i, 16, 65536);
649                 if (wrch) {
650                         CHN_LOCK(wrch);
651                         chn_setblocksize(wrch, 2, *arg_i);
652                         CHN_UNLOCK(wrch);
653                 }
654                 if (rdch) {
655                         CHN_LOCK(rdch);
656                         chn_setblocksize(rdch, 2, *arg_i);
657                         CHN_UNLOCK(rdch);
658                 }
659                 break;
660
661         case SNDCTL_DSP_RESET:
662                 DEB(printf("dsp reset\n"));
663                 if (wrch)
664                         chn_abort(wrch);
665                 if (rdch)
666                         chn_abort(rdch);
667                 break;
668
669         case SNDCTL_DSP_SYNC:
670                 DEB(printf("dsp sync\n"));
671                 /* chn_sync may sleep */
672                 if (wrch) {
673                         CHN_LOCK(wrch);
674                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
675                         CHN_UNLOCK(wrch);
676                 }
677                 break;
678
679         case SNDCTL_DSP_SPEED:
680                 /* chn_setspeed may sleep */
681                 tmp = 0;
682                 if (wrch) {
683                         CHN_LOCK(wrch);
684                         ret = chn_setspeed(wrch, *arg_i);
685                         tmp = wrch->speed;
686                         CHN_UNLOCK(wrch);
687                 }
688                 if (rdch && ret == 0) {
689                         CHN_LOCK(rdch);
690                         ret = chn_setspeed(rdch, *arg_i);
691                         if (tmp == 0)
692                                 tmp = rdch->speed;
693                         CHN_UNLOCK(rdch);
694                 }
695                 *arg_i = tmp;
696                 break;
697
698         case SOUND_PCM_READ_RATE:
699                 *arg_i = wrch? wrch->speed : rdch->speed;
700                 break;
701
702         case SNDCTL_DSP_STEREO:
703                 tmp = -1;
704                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
705                 if (wrch) {
706                         CHN_LOCK(wrch);
707                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
708                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
709                         CHN_UNLOCK(wrch);
710                 }
711                 if (rdch && ret == 0) {
712                         CHN_LOCK(rdch);
713                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
714                         if (tmp == -1)
715                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
716                         CHN_UNLOCK(rdch);
717                 }
718                 *arg_i = tmp;
719                 break;
720
721         case SOUND_PCM_WRITE_CHANNELS:
722 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
723                 if (*arg_i != 0) {
724                         tmp = 0;
725                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
726                         if (wrch) {
727                                 CHN_LOCK(wrch);
728                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
729                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
730                                 CHN_UNLOCK(wrch);
731                         }
732                         if (rdch && ret == 0) {
733                                 CHN_LOCK(rdch);
734                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
735                                 if (tmp == 0)
736                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
737                                 CHN_UNLOCK(rdch);
738                         }
739                         *arg_i = tmp;
740                 } else {
741                         *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
742                 }
743                 break;
744
745         case SOUND_PCM_READ_CHANNELS:
746                 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
747                 break;
748
749         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
750                 *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
751                 break ;
752
753         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
754                 /* XXX locking */
755                 if ((*arg_i != AFMT_QUERY)) {
756                         tmp = 0;
757                         if (wrch) {
758                                 CHN_LOCK(wrch);
759                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
760                                 tmp = wrch->format & ~AFMT_STEREO;
761                                 CHN_UNLOCK(wrch);
762                         }
763                         if (rdch && ret == 0) {
764                                 CHN_LOCK(rdch);
765                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
766                                 if (tmp == 0)
767                                         tmp = rdch->format & ~AFMT_STEREO;
768                                 CHN_UNLOCK(rdch);
769                         }
770                         *arg_i = tmp;
771                 } else
772                         *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
773                 break;
774
775         case SNDCTL_DSP_SETFRAGMENT:
776                 /* XXX locking */
777                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
778                 {
779                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
780                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
781                         u_int32_t fragsz;
782
783                         RANGE(fragln, 4, 16);
784                         fragsz = 1 << fragln;
785
786                         if (maxfrags == 0)
787                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
788                         if (maxfrags < 2) {
789                                 ret = EINVAL;
790                                 break;
791                         }
792                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
793                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
794
795                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
796                         if (rdch) {
797                                 CHN_LOCK(rdch);
798                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
799                                 maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
800                                 fragsz = sndbuf_getblksz(rdch->bufsoft);
801                                 CHN_UNLOCK(rdch);
802                         }
803                         if (wrch && ret == 0) {
804                                 CHN_LOCK(wrch);
805                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
806                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
807                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
808                                 CHN_UNLOCK(wrch);
809                         }
810
811                         fragln = 0;
812                         while (fragsz > 1) {
813                                 fragln++;
814                                 fragsz >>= 1;
815                         }
816                         *arg_i = (maxfrags << 16) | fragln;
817                 }
818                 break;
819
820         case SNDCTL_DSP_GETISPACE:
821                 /* return the size of data available in the input queue */
822                 {
823                         audio_buf_info *a = (audio_buf_info *)arg;
824                         if (rdch) {
825                                 struct snd_dbuf *bs = rdch->bufsoft;
826
827                                 CHN_LOCK(rdch);
828                                 a->bytes = sndbuf_getready(bs);
829                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
830                                 a->fragstotal = sndbuf_getblkcnt(bs);
831                                 a->fragsize = sndbuf_getblksz(bs);
832                                 CHN_UNLOCK(rdch);
833                         }
834                 }
835                 break;
836
837         case SNDCTL_DSP_GETOSPACE:
838                 /* return space available in the output queue */
839                 {
840                         audio_buf_info *a = (audio_buf_info *)arg;
841                         if (wrch) {
842                                 struct snd_dbuf *bs = wrch->bufsoft;
843
844                                 CHN_LOCK(wrch);
845                                 chn_wrupdate(wrch);
846                                 a->bytes = sndbuf_getfree(bs);
847                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
848                                 a->fragstotal = sndbuf_getblkcnt(bs);
849                                 a->fragsize = sndbuf_getblksz(bs);
850                                 CHN_UNLOCK(wrch);
851                         }
852                 }
853                 break;
854
855         case SNDCTL_DSP_GETIPTR:
856                 {
857                         count_info *a = (count_info *)arg;
858                         if (rdch) {
859                                 struct snd_dbuf *bs = rdch->bufsoft;
860
861                                 CHN_LOCK(rdch);
862                                 chn_rdupdate(rdch);
863                                 a->bytes = sndbuf_gettotal(bs);
864                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
865                                 a->ptr = sndbuf_getreadyptr(bs);
866                                 rdch->blocks = sndbuf_getblocks(bs);
867                                 CHN_UNLOCK(rdch);
868                         } else
869                                 ret = EINVAL;
870                 }
871                 break;
872
873         case SNDCTL_DSP_GETOPTR:
874                 {
875                         count_info *a = (count_info *)arg;
876                         if (wrch) {
877                                 struct snd_dbuf *bs = wrch->bufsoft;
878
879                                 CHN_LOCK(wrch);
880                                 chn_wrupdate(wrch);
881                                 a->bytes = sndbuf_gettotal(bs);
882                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
883                                 a->ptr = sndbuf_getreadyptr(bs);
884                                 wrch->blocks = sndbuf_getblocks(bs);
885                                 CHN_UNLOCK(wrch);
886                         } else
887                                 ret = EINVAL;
888                 }
889                 break;
890
891         case SNDCTL_DSP_GETCAPS:
892                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
893                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
894                         *arg_i |= DSP_CAP_DUPLEX;
895                 break;
896
897         case SOUND_PCM_READ_BITS:
898                 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
899                 break;
900
901         case SNDCTL_DSP_SETTRIGGER:
902                 if (rdch) {
903                         CHN_LOCK(rdch);
904                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
905                         if (*arg_i & PCM_ENABLE_INPUT)
906                                 chn_start(rdch, 1);
907                         else
908                                 rdch->flags |= CHN_F_NOTRIGGER;
909                         CHN_UNLOCK(rdch);
910                 }
911                 if (wrch) {
912                         CHN_LOCK(wrch);
913                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
914                         if (*arg_i & PCM_ENABLE_OUTPUT)
915                                 chn_start(wrch, 1);
916                         else
917                                 wrch->flags |= CHN_F_NOTRIGGER;
918                         CHN_UNLOCK(wrch);
919                 }
920                 break;
921
922         case SNDCTL_DSP_GETTRIGGER:
923                 *arg_i = 0;
924                 if (wrch && wrch->flags & CHN_F_TRIGGERED)
925                         *arg_i |= PCM_ENABLE_OUTPUT;
926                 if (rdch && rdch->flags & CHN_F_TRIGGERED)
927                         *arg_i |= PCM_ENABLE_INPUT;
928                 break;
929
930         case SNDCTL_DSP_GETODELAY:
931                 if (wrch) {
932                         struct snd_dbuf *b = wrch->bufhard;
933                         struct snd_dbuf *bs = wrch->bufsoft;
934
935                         CHN_LOCK(wrch);
936                         chn_wrupdate(wrch);
937                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
938                         CHN_UNLOCK(wrch);
939                 } else
940                         ret = EINVAL;
941                 break;
942
943         case SNDCTL_DSP_POST:
944                 if (wrch) {
945                         CHN_LOCK(wrch);
946                         wrch->flags &= ~CHN_F_NOTRIGGER;
947                         chn_start(wrch, 1);
948                         CHN_UNLOCK(wrch);
949                 }
950                 break;
951
952         case SNDCTL_DSP_MAPINBUF:
953         case SNDCTL_DSP_MAPOUTBUF:
954         case SNDCTL_DSP_SETSYNCRO:
955                 /* undocumented */
956
957         case SNDCTL_DSP_SUBDIVIDE:
958         case SOUND_PCM_WRITE_FILTER:
959         case SOUND_PCM_READ_FILTER:
960                 /* dunno what these do, don't sound important */
961         default:
962                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
963                 ret = EINVAL;
964                 break;
965         }
966         relchns(i_dev, rdch, wrch, 0);
967         splx(s);
968         return ret;
969 }
970
971 static int
972 dsp_poll(dev_t i_dev, int events, struct thread *td)
973 {
974         struct pcm_channel *wrch = NULL, *rdch = NULL;
975         intrmask_t s;
976         int ret, e;
977
978         s = spltty();
979         ret = 0;
980         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
981
982         if (wrch) {
983                 e = (events & (POLLOUT | POLLWRNORM));
984                 if (e)
985                         ret |= chn_poll(wrch, e, td->td_proc);
986         }
987         if (rdch) {
988                 e = (events & (POLLIN | POLLRDNORM));
989                 if (e)
990                         ret |= chn_poll(rdch, e, td->td_proc);
991         }
992         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
993
994         splx(s);
995         return ret;
996 }
997
998 static int
999 dsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot)
1000 {
1001         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1002         intrmask_t s;
1003         int ret;
1004
1005         if (nprot & PROT_EXEC)
1006                 return -1;
1007
1008         s = spltty();
1009         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1010 #if 0
1011         /*
1012          * XXX the linux api uses the nprot to select read/write buffer
1013          * our vm system doesn't allow this, so force write buffer
1014          */
1015
1016         if (wrch && (nprot & PROT_WRITE)) {
1017                 c = wrch;
1018         } else if (rdch && (nprot & PROT_READ)) {
1019                 c = rdch;
1020         } else {
1021                 splx(s);
1022                 return -1;
1023         }
1024 #else
1025         c = wrch;
1026 #endif
1027
1028         if (c == NULL) {
1029                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1030                 splx(s);
1031                 return -1;
1032         }
1033
1034         if (offset >= sndbuf_getsize(c->bufsoft)) {
1035                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1036                 splx(s);
1037                 return -1;
1038         }
1039
1040         if (!(c->flags & CHN_F_MAPPED))
1041                 c->flags |= CHN_F_MAPPED;
1042
1043         ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
1044         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1045
1046         splx(s);
1047         return ret;
1048 }
1049
1050 int
1051 dsp_register(int unit, int channel)
1052 {
1053         cdevsw_add(&dsp_cdevsw, 
1054                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1055         cdevsw_add(&dsp_cdevsw, 
1056                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1057         cdevsw_add(&dsp_cdevsw, 
1058                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1059         cdevsw_add(&dsp_cdevsw, 
1060                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1061         make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
1062                  UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
1063         make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
1064                  UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
1065         make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
1066                  UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
1067
1068         return 0;
1069 }
1070
1071 int
1072 dsp_registerrec(int unit, int channel)
1073 {
1074         cdevsw_add(&dsp_cdevsw, 
1075                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1076         make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
1077                  UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
1078
1079         return 0;
1080 }
1081
1082 int
1083 dsp_unregister(int unit, int channel)
1084 {
1085         cdevsw_remove(&dsp_cdevsw, 
1086                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1087         cdevsw_remove(&dsp_cdevsw, 
1088                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1089         cdevsw_remove(&dsp_cdevsw, 
1090                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1091         cdevsw_remove(&dsp_cdevsw, 
1092                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1093         return 0;
1094 }
1095
1096 int
1097 dsp_unregisterrec(int unit, int channel)
1098 {
1099         cdevsw_remove(&dsp_cdevsw, 
1100                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1101         return 0;
1102 }
1103
1104 #ifdef USING_DEVFS
1105 static void
1106 dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
1107 {
1108         dev_t pdev;
1109         int i, cont, unit, devtype;
1110         int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1111         char *devnames[3] = {"dsp", "dspW", "audio"};
1112
1113         if (*dev != NODEV)
1114                 return;
1115         if (pcm_devclass == NULL)
1116                 return;
1117
1118         devtype = 0;
1119         unit = -1;
1120         for (i = 0; (i < 3) && (unit == -1); i++) {
1121                 devtype = devtypes[i];
1122                 if (strcmp(name, devnames[i]) == 0) {
1123                         unit = snd_unit;
1124                 } else {
1125                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1126                                 unit = -1;
1127                 }
1128         }
1129         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1130                 return;
1131
1132         cont = 1;
1133         for (i = 0; cont; i++) {
1134                 pdev = make_adhoc_dev(&dsp_cdevsw, PCMMKMINOR(unit, devtype, i));
1135                 if (pdev->si_flags & SI_NAMED) {
1136                         if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1137                                 *dev = pdev;
1138                                 return;
1139                         }
1140                 } else {
1141                         cont = 0;
1142                 }
1143         }
1144 }
1145
1146 static void
1147 dsp_sysinit(void *p)
1148 {
1149         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1150 }
1151
1152 static void
1153 dsp_sysuninit(void *p)
1154 {
1155         if (dsp_ehtag != NULL)
1156                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1157 }
1158
1159 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1160 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1161 #endif
1162
1163