Change the kernel dev_t, representing a pointer to a specinfo structure,
[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.12 2006/09/10 01:26:37 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.12 2006/09/10 01:26:37 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 dev_ops dsp_ops = {
48         { "dsp", SND_CDEV_MAJOR, 0 },
49         .d_open =       dsp_open,
50         .d_close =      dsp_close,
51         .d_read =       dsp_read,
52         .d_write =      dsp_write,
53         .d_ioctl =      dsp_ioctl,
54         .d_poll =       dsp_poll,
55         .d_mmap =       dsp_mmap,
56 };
57
58 #ifdef USING_DEVFS
59 static eventhandler_tag dsp_ehtag;
60 #endif
61
62 static struct snddev_info *
63 dsp_get_info(cdev_t dev)
64 {
65         struct snddev_info *d;
66         int unit;
67
68         unit = PCMUNIT(dev);
69         if (unit >= devclass_get_maxunit(pcm_devclass))
70                 return NULL;
71         d = devclass_get_softc(pcm_devclass, unit);
72
73         return d;
74 }
75
76 static u_int32_t
77 dsp_get_flags(cdev_t dev)
78 {
79         device_t bdev;
80         int unit;
81
82         unit = PCMUNIT(dev);
83         if (unit >= devclass_get_maxunit(pcm_devclass))
84                 return 0xffffffff;
85         bdev = devclass_get_device(pcm_devclass, unit);
86
87         return pcm_getflags(bdev);
88 }
89
90 static void
91 dsp_set_flags(cdev_t dev, u_int32_t flags)
92 {
93         device_t bdev;
94         int unit;
95
96         unit = PCMUNIT(dev);
97         if (unit >= devclass_get_maxunit(pcm_devclass))
98                 return;
99         bdev = devclass_get_device(pcm_devclass, unit);
100
101         pcm_setflags(bdev, flags);
102 }
103
104 /*
105  * return the channels channels associated with an open device instance.
106  * set the priority if the device is simplex and one direction (only) is
107  * specified.
108  * lock channels specified.
109  */
110 static int
111 getchns(cdev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
112 {
113         struct snddev_info *d;
114         u_int32_t flags;
115
116         flags = dsp_get_flags(dev);
117         d = dsp_get_info(dev);
118         pcm_lock(d);
119         pcm_inprog(d, 1);
120         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
121                 ("getchns: read and write both prioritised"));
122
123         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
124                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
125                 dsp_set_flags(dev, flags);
126         }
127
128         *rdch = dev->si_drv1;
129         *wrch = dev->si_drv2;
130         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
131                 if (prio) {
132                         if (*rdch && flags & SD_F_PRIO_WR) {
133                                 dev->si_drv1 = NULL;
134                                 *rdch = pcm_getfakechan(d);
135                         } else if (*wrch && flags & SD_F_PRIO_RD) {
136                                 dev->si_drv2 = NULL;
137                                 *wrch = pcm_getfakechan(d);
138                         }
139                 }
140
141                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
142         }
143         pcm_unlock(d);
144
145         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
146                 CHN_LOCK(*rdch);
147         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
148                 CHN_LOCK(*wrch);
149
150         return 0;
151 }
152
153 /* unlock specified channels */
154 static void
155 relchns(cdev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
156 {
157         struct snddev_info *d;
158
159         d = dsp_get_info(dev);
160         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
161                 CHN_UNLOCK(wrch);
162         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
163                 CHN_UNLOCK(rdch);
164         pcm_lock(d);
165         pcm_inprog(d, -1);
166         pcm_unlock(d);
167 }
168
169 static int
170 dsp_open(struct dev_open_args *ap)
171 {
172         cdev_t dev = ap->a_head.a_dev;
173         struct pcm_channel *rdch, *wrch;
174         struct snddev_info *d;
175         u_int32_t fmt;
176         int devtype;
177
178         crit_enter();
179         d = dsp_get_info(dev);
180         devtype = PCMDEV(dev);
181
182         /* decide default format */
183         switch (devtype) {
184         case SND_DEV_DSP16:
185                 fmt = AFMT_S16_LE;
186                 break;
187
188         case SND_DEV_DSP:
189                 fmt = AFMT_U8;
190                 break;
191
192         case SND_DEV_AUDIO:
193                 fmt = AFMT_MU_LAW;
194                 break;
195
196         case SND_DEV_NORESET:
197                 fmt = 0;
198                 break;
199
200         case SND_DEV_DSPREC:
201                 fmt = AFMT_U8;
202                 if (ap->a_oflags & FWRITE) {
203                         crit_exit();
204                         return EINVAL;
205                 }
206                 break;
207
208         default:
209                 panic("impossible devtype %d", devtype);
210         }
211
212         /* lock snddev so nobody else can monkey with it */
213         pcm_lock(d);
214
215         rdch = dev->si_drv1;
216         wrch = dev->si_drv2;
217
218         if ((dsp_get_flags(dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
219                 /* simplex device, already open, exit */
220                 pcm_unlock(d);
221                 crit_exit();
222                 return EBUSY;
223         }
224
225         if (((ap->a_oflags & FREAD) && rdch) ||
226             ((ap->a_oflags & FWRITE) && wrch)) {
227                 /* device already open in one or both directions */
228                 pcm_unlock(d);
229                 crit_exit();
230                 return EBUSY;
231         }
232
233         /*  if we get here, the open request is valid */
234         if (ap->a_oflags & FREAD) {
235                 /* open for read */
236                 if (devtype == SND_DEV_DSPREC)
237                         rdch = pcm_chnalloc(d, PCMDIR_REC, curproc->p_pid, PCMCHAN(dev));
238                 else
239                         rdch = pcm_chnalloc(d, PCMDIR_REC, curproc->p_pid, -1);
240                 if (!rdch) {
241                         /* no channel available, exit */
242                         pcm_unlock(d);
243                         crit_exit();
244                         return EBUSY;
245                 }
246                 /* got a channel, already locked for us */
247         }
248
249         if (ap->a_oflags & FWRITE) {
250                 /* open for write */
251                 wrch = pcm_chnalloc(d, PCMDIR_PLAY, curproc->p_pid, -1);
252                 if (!wrch) {
253                         /* no channel available */
254                         if (rdch && (ap->a_oflags & FREAD)) {
255                                 /* just opened a read channel, release it */
256                                 pcm_chnrelease(rdch);
257                         }
258                                 /* exit */
259                         pcm_unlock(d);
260                         crit_exit();
261                         return EBUSY;
262                 }
263                 /* got a channel, already locked for us */
264         }
265
266         dev->si_drv1 = rdch;
267         dev->si_drv2 = wrch;
268         pcm_unlock(d);
269         /* finished with snddev, new channels still locked */
270
271         /* bump refcounts, reset and unlock any channels that we just opened */
272         if (ap->a_oflags & FREAD) {
273                 if (chn_reset(rdch, fmt)) {
274                         pcm_lock(d);
275                         pcm_chnrelease(rdch);
276                         if (wrch && (ap->a_oflags & FWRITE))
277                                 pcm_chnrelease(wrch);
278                         pcm_unlock(d);
279                         crit_exit();
280                         return ENODEV;
281                 }
282 #if 0
283                 /* removed, will be passed as an IO_ flag */
284                 if (ap->a_oflags & O_NONBLOCK)
285                         rdch->flags |= CHN_F_NBIO;
286 #endif
287                 pcm_chnref(rdch, 1);
288                 CHN_UNLOCK(rdch);
289         }
290         if (ap->a_oflags & FWRITE) {
291                 if (chn_reset(wrch, fmt)) {
292                         pcm_lock(d);
293                         pcm_chnrelease(wrch);
294                         if (ap->a_oflags & FREAD) {
295                                 CHN_LOCK(rdch);
296                                 pcm_chnref(rdch, -1);
297                                 pcm_chnrelease(rdch);
298                                 CHN_UNLOCK(rdch);
299                         }
300                         pcm_unlock(d);
301                         crit_exit();
302                         return ENODEV;
303                 }
304 #if 0
305                 /* removed, will be passed as an IO_ flag */
306                 if (ap->a_oflags & O_NONBLOCK)
307                         wrch->flags |= CHN_F_NBIO;
308 #endif
309                 pcm_chnref(wrch, 1);
310                 CHN_UNLOCK(wrch);
311         }
312         crit_exit();
313         return 0;
314 }
315
316 static int
317 dsp_close(struct dev_close_args *ap)
318 {
319         cdev_t dev = ap->a_head.a_dev;
320         struct pcm_channel *rdch, *wrch;
321         struct snddev_info *d;
322         int exit;
323
324         crit_enter();
325         d = dsp_get_info(dev);
326         pcm_lock(d);
327         rdch = dev->si_drv1;
328         wrch = dev->si_drv2;
329
330         exit = 0;
331
332         /* decrement refcount for each channel, exit if nonzero */
333         if (rdch) {
334                 CHN_LOCK(rdch);
335                 if (pcm_chnref(rdch, -1) > 0) {
336                         CHN_UNLOCK(rdch);
337                         exit = 1;
338                 }
339         }
340         if (wrch) {
341                 CHN_LOCK(wrch);
342                 if (pcm_chnref(wrch, -1) > 0) {
343                         CHN_UNLOCK(wrch);
344                         exit = 1;
345                 }
346         }
347         if (exit) {
348                 pcm_unlock(d);
349                 crit_exit();
350                 return 0;
351         }
352
353         /* both refcounts are zero, abort and release */
354
355         if (pcm_getfakechan(d))
356                 pcm_getfakechan(d)->flags = 0;
357
358         dev->si_drv1 = NULL;
359         dev->si_drv2 = NULL;
360
361         dsp_set_flags(dev, dsp_get_flags(dev) & ~SD_F_TRANSIENT);
362         pcm_unlock(d);
363
364         if (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_flush(wrch); /* may sleep */
372                 wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
373                 chn_reset(wrch, 0);
374                 pcm_chnrelease(wrch);
375         }
376
377         crit_exit();
378         return 0;
379 }
380
381 static int
382 dsp_read(struct dev_read_args *ap)
383 {
384         cdev_t dev = ap->a_head.a_dev;
385         struct pcm_channel *rdch, *wrch;
386         int ret;
387
388         crit_enter();
389         getchns(dev, &rdch, &wrch, SD_F_PRIO_RD);
390
391         KASSERT(rdch, ("dsp_read: nonexistant channel"));
392         KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
393
394         if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
395                 relchns(dev, rdch, wrch, SD_F_PRIO_RD);
396                 crit_exit();
397                 return EINVAL;
398         }
399         if (!(rdch->flags & CHN_F_RUNNING))
400                 rdch->flags |= CHN_F_RUNNING;
401         ret = chn_read(rdch, ap->a_uio, ap->a_ioflag);
402         relchns(dev, rdch, wrch, SD_F_PRIO_RD);
403
404         crit_exit();
405         return ret;
406 }
407
408 static int
409 dsp_write(struct dev_write_args *ap)
410 {
411         cdev_t dev = ap->a_head.a_dev;
412         struct pcm_channel *rdch, *wrch;
413         int ret;
414
415         crit_enter();
416         getchns(dev, &rdch, &wrch, SD_F_PRIO_WR);
417
418         KASSERT(wrch, ("dsp_write: nonexistant channel"));
419         KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
420
421         if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
422                 relchns(dev, rdch, wrch, SD_F_PRIO_WR);
423                 crit_exit();
424                 return EINVAL;
425         }
426         if (!(wrch->flags & CHN_F_RUNNING))
427                 wrch->flags |= CHN_F_RUNNING;
428         ret = chn_write(wrch, ap->a_uio, ap->a_ioflag);
429         relchns(dev, rdch, wrch, SD_F_PRIO_WR);
430
431         crit_exit();
432         return ret;
433 }
434
435 static int
436 dsp_ioctl(struct dev_ioctl_args *ap)
437 {
438         cdev_t dev = ap->a_head.a_dev;
439         u_long cmd = ap->a_cmd;
440         caddr_t arg = ap->a_data;
441         struct pcm_channel *wrch, *rdch;
442         struct snddev_info *d;
443         int kill;
444         int ret = 0, *arg_i = (int *)arg, tmp;
445
446         /*
447          * this is an evil hack to allow broken apps to perform mixer ioctls
448          * on dsp devices.
449          */
450
451         if (IOCGROUP(cmd) == 'M') {
452                 cdev_t pdev;
453
454                 pdev = make_adhoc_dev(&dsp_ops, 
455                                 PCMMKMINOR(PCMUNIT(dev), SND_DEV_CTL, 0));
456                 ap->a_head.a_dev = pdev;
457                 return mixer_ioctl(ap);
458         }
459
460         crit_enter();
461         d = dsp_get_info(dev);
462         getchns(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(dev, rdch, wrch, 0);
471                 crit_exit();
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                         cdev_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(dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
578                         pdev = make_adhoc_dev(&dsp_ops, PCMMKMINOR(PCMUNIT(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                 /*
620                  * set/clear non-blocking I/O.  WARNING: non-blocking I/O
621                  * can also be set with FIONBIO
622                  */
623                 if (rdch)
624                         rdch->flags &= ~CHN_F_NBIO;
625                 if (wrch)
626                         wrch->flags &= ~CHN_F_NBIO;
627                 if (*arg_i) {
628                         if (rdch)
629                                 rdch->flags |= CHN_F_NBIO;
630                         if (wrch)
631                                 wrch->flags |= CHN_F_NBIO;
632                 }
633                 break;
634
635         /*
636          * Finally, here is the linux-compatible ioctl interface
637          */
638 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
639         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
640         case SNDCTL_DSP_GETBLKSIZE:
641                 if (wrch)
642                         *arg_i = sndbuf_getblksz(wrch->bufsoft);
643                 else if (rdch)
644                         *arg_i = sndbuf_getblksz(rdch->bufsoft);
645                 else
646                         *arg_i = 0;
647                 break ;
648
649         case SNDCTL_DSP_SETBLKSIZE:
650                 RANGE(*arg_i, 16, 65536);
651                 if (wrch) {
652                         CHN_LOCK(wrch);
653                         chn_setblocksize(wrch, 2, *arg_i);
654                         CHN_UNLOCK(wrch);
655                 }
656                 if (rdch) {
657                         CHN_LOCK(rdch);
658                         chn_setblocksize(rdch, 2, *arg_i);
659                         CHN_UNLOCK(rdch);
660                 }
661                 break;
662
663         case SNDCTL_DSP_RESET:
664                 DEB(printf("dsp reset\n"));
665                 if (wrch)
666                         chn_abort(wrch);
667                 if (rdch)
668                         chn_abort(rdch);
669                 break;
670
671         case SNDCTL_DSP_SYNC:
672                 DEB(printf("dsp sync\n"));
673                 /* chn_sync may sleep */
674                 if (wrch) {
675                         CHN_LOCK(wrch);
676                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
677                         CHN_UNLOCK(wrch);
678                 }
679                 break;
680
681         case SNDCTL_DSP_SPEED:
682                 /* chn_setspeed may sleep */
683                 tmp = 0;
684                 if (wrch) {
685                         CHN_LOCK(wrch);
686                         ret = chn_setspeed(wrch, *arg_i);
687                         tmp = wrch->speed;
688                         CHN_UNLOCK(wrch);
689                 }
690                 if (rdch && ret == 0) {
691                         CHN_LOCK(rdch);
692                         ret = chn_setspeed(rdch, *arg_i);
693                         if (tmp == 0)
694                                 tmp = rdch->speed;
695                         CHN_UNLOCK(rdch);
696                 }
697                 *arg_i = tmp;
698                 break;
699
700         case SOUND_PCM_READ_RATE:
701                 *arg_i = wrch? wrch->speed : rdch->speed;
702                 break;
703
704         case SNDCTL_DSP_STEREO:
705                 tmp = -1;
706                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
707                 if (wrch) {
708                         CHN_LOCK(wrch);
709                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
710                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
711                         CHN_UNLOCK(wrch);
712                 }
713                 if (rdch && ret == 0) {
714                         CHN_LOCK(rdch);
715                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
716                         if (tmp == -1)
717                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
718                         CHN_UNLOCK(rdch);
719                 }
720                 *arg_i = tmp;
721                 break;
722
723         case SOUND_PCM_WRITE_CHANNELS:
724 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
725                 if (*arg_i != 0) {
726                         tmp = 0;
727                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
728                         if (wrch) {
729                                 CHN_LOCK(wrch);
730                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
731                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
732                                 CHN_UNLOCK(wrch);
733                         }
734                         if (rdch && ret == 0) {
735                                 CHN_LOCK(rdch);
736                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
737                                 if (tmp == 0)
738                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
739                                 CHN_UNLOCK(rdch);
740                         }
741                         *arg_i = tmp;
742                 } else {
743                         *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
744                 }
745                 break;
746
747         case SOUND_PCM_READ_CHANNELS:
748                 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
749                 break;
750
751         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
752                 *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
753                 break ;
754
755         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
756                 /* XXX locking */
757                 if ((*arg_i != AFMT_QUERY)) {
758                         tmp = 0;
759                         if (wrch) {
760                                 CHN_LOCK(wrch);
761                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
762                                 tmp = wrch->format & ~AFMT_STEREO;
763                                 CHN_UNLOCK(wrch);
764                         }
765                         if (rdch && ret == 0) {
766                                 CHN_LOCK(rdch);
767                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
768                                 if (tmp == 0)
769                                         tmp = rdch->format & ~AFMT_STEREO;
770                                 CHN_UNLOCK(rdch);
771                         }
772                         *arg_i = tmp;
773                 } else
774                         *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
775                 break;
776
777         case SNDCTL_DSP_SETFRAGMENT:
778                 /* XXX locking */
779                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
780                 {
781                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
782                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
783                         u_int32_t fragsz;
784
785                         RANGE(fragln, 4, 16);
786                         fragsz = 1 << fragln;
787
788                         if (maxfrags == 0)
789                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
790                         if (maxfrags < 2) {
791                                 ret = EINVAL;
792                                 break;
793                         }
794                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
795                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
796
797                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
798                         if (rdch) {
799                                 CHN_LOCK(rdch);
800                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
801                                 maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
802                                 fragsz = sndbuf_getblksz(rdch->bufsoft);
803                                 CHN_UNLOCK(rdch);
804                         }
805                         if (wrch && ret == 0) {
806                                 CHN_LOCK(wrch);
807                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
808                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
809                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
810                                 CHN_UNLOCK(wrch);
811                         }
812
813                         fragln = 0;
814                         while (fragsz > 1) {
815                                 fragln++;
816                                 fragsz >>= 1;
817                         }
818                         *arg_i = (maxfrags << 16) | fragln;
819                 }
820                 break;
821
822         case SNDCTL_DSP_GETISPACE:
823                 /* return the size of data available in the input queue */
824                 {
825                         audio_buf_info *a = (audio_buf_info *)arg;
826                         if (rdch) {
827                                 struct snd_dbuf *bs = rdch->bufsoft;
828
829                                 CHN_LOCK(rdch);
830                                 a->bytes = sndbuf_getready(bs);
831                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
832                                 a->fragstotal = sndbuf_getblkcnt(bs);
833                                 a->fragsize = sndbuf_getblksz(bs);
834                                 CHN_UNLOCK(rdch);
835                         }
836                 }
837                 break;
838
839         case SNDCTL_DSP_GETOSPACE:
840                 /* return space available in the output queue */
841                 {
842                         audio_buf_info *a = (audio_buf_info *)arg;
843                         if (wrch) {
844                                 struct snd_dbuf *bs = wrch->bufsoft;
845
846                                 CHN_LOCK(wrch);
847                                 chn_wrupdate(wrch);
848                                 a->bytes = sndbuf_getfree(bs);
849                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
850                                 a->fragstotal = sndbuf_getblkcnt(bs);
851                                 a->fragsize = sndbuf_getblksz(bs);
852                                 CHN_UNLOCK(wrch);
853                         }
854                 }
855                 break;
856
857         case SNDCTL_DSP_GETIPTR:
858                 {
859                         count_info *a = (count_info *)arg;
860                         if (rdch) {
861                                 struct snd_dbuf *bs = rdch->bufsoft;
862
863                                 CHN_LOCK(rdch);
864                                 chn_rdupdate(rdch);
865                                 a->bytes = sndbuf_gettotal(bs);
866                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
867                                 a->ptr = sndbuf_getreadyptr(bs);
868                                 rdch->blocks = sndbuf_getblocks(bs);
869                                 CHN_UNLOCK(rdch);
870                         } else
871                                 ret = EINVAL;
872                 }
873                 break;
874
875         case SNDCTL_DSP_GETOPTR:
876                 {
877                         count_info *a = (count_info *)arg;
878                         if (wrch) {
879                                 struct snd_dbuf *bs = wrch->bufsoft;
880
881                                 CHN_LOCK(wrch);
882                                 chn_wrupdate(wrch);
883                                 a->bytes = sndbuf_gettotal(bs);
884                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
885                                 a->ptr = sndbuf_getreadyptr(bs);
886                                 wrch->blocks = sndbuf_getblocks(bs);
887                                 CHN_UNLOCK(wrch);
888                         } else
889                                 ret = EINVAL;
890                 }
891                 break;
892
893         case SNDCTL_DSP_GETCAPS:
894                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
895                 if (rdch && wrch && !(dsp_get_flags(dev) & SD_F_SIMPLEX))
896                         *arg_i |= DSP_CAP_DUPLEX;
897                 break;
898
899         case SOUND_PCM_READ_BITS:
900                 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
901                 break;
902
903         case SNDCTL_DSP_SETTRIGGER:
904                 if (rdch) {
905                         CHN_LOCK(rdch);
906                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
907                         if (*arg_i & PCM_ENABLE_INPUT)
908                                 chn_start(rdch, 1);
909                         else
910                                 rdch->flags |= CHN_F_NOTRIGGER;
911                         CHN_UNLOCK(rdch);
912                 }
913                 if (wrch) {
914                         CHN_LOCK(wrch);
915                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
916                         if (*arg_i & PCM_ENABLE_OUTPUT)
917                                 chn_start(wrch, 1);
918                         else
919                                 wrch->flags |= CHN_F_NOTRIGGER;
920                         CHN_UNLOCK(wrch);
921                 }
922                 break;
923
924         case SNDCTL_DSP_GETTRIGGER:
925                 *arg_i = 0;
926                 if (wrch && wrch->flags & CHN_F_TRIGGERED)
927                         *arg_i |= PCM_ENABLE_OUTPUT;
928                 if (rdch && rdch->flags & CHN_F_TRIGGERED)
929                         *arg_i |= PCM_ENABLE_INPUT;
930                 break;
931
932         case SNDCTL_DSP_GETODELAY:
933                 if (wrch) {
934                         struct snd_dbuf *b = wrch->bufhard;
935                         struct snd_dbuf *bs = wrch->bufsoft;
936
937                         CHN_LOCK(wrch);
938                         chn_wrupdate(wrch);
939                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
940                         CHN_UNLOCK(wrch);
941                 } else
942                         ret = EINVAL;
943                 break;
944
945         case SNDCTL_DSP_POST:
946                 if (wrch) {
947                         CHN_LOCK(wrch);
948                         wrch->flags &= ~CHN_F_NOTRIGGER;
949                         chn_start(wrch, 1);
950                         CHN_UNLOCK(wrch);
951                 }
952                 break;
953
954         case SNDCTL_DSP_MAPINBUF:
955         case SNDCTL_DSP_MAPOUTBUF:
956         case SNDCTL_DSP_SETSYNCRO:
957                 /* undocumented */
958
959         case SNDCTL_DSP_SUBDIVIDE:
960         case SOUND_PCM_WRITE_FILTER:
961         case SOUND_PCM_READ_FILTER:
962                 /* dunno what these do, don't sound important */
963         default:
964                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
965                 ret = EINVAL;
966                 break;
967         }
968         relchns(dev, rdch, wrch, 0);
969         crit_exit();
970         return ret;
971 }
972
973 static int
974 dsp_poll(struct dev_poll_args *ap)
975 {
976         cdev_t dev = ap->a_head.a_dev;
977         struct pcm_channel *wrch = NULL, *rdch = NULL;
978         int ret, e;
979
980         crit_enter();
981         ret = 0;
982         getchns(dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
983
984         if (wrch) {
985                 e = (ap->a_events & (POLLOUT | POLLWRNORM));
986                 if (e)
987                         ret |= chn_poll(wrch, e);
988         }
989         if (rdch) {
990                 e = (ap->a_events & (POLLIN | POLLRDNORM));
991                 if (e)
992                         ret |= chn_poll(rdch, e);
993         }
994         relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
995
996         crit_exit();
997         ap->a_events = ret;
998         return (0);
999 }
1000
1001 static int
1002 dsp_mmap(struct dev_mmap_args *ap)
1003 {
1004         cdev_t dev = ap->a_head.a_dev;
1005         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1006         int ret;
1007
1008         if (ap->a_nprot & PROT_EXEC)
1009                 return EINVAL;
1010
1011         crit_enter();
1012         getchns(dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1013 #if 0
1014         /*
1015          * XXX the linux api uses the nprot to select read/write buffer
1016          * our vm system doesn't allow this, so force write buffer
1017          */
1018
1019         if (wrch && (ap->a_nprot & PROT_WRITE)) {
1020                 c = wrch;
1021         } else if (rdch && (ap->a_nprot & PROT_READ)) {
1022                 c = rdch;
1023         } else {
1024                 crit_exit();
1025                 return EINVAL;
1026         }
1027 #else
1028         c = wrch;
1029 #endif
1030
1031         if (c == NULL) {
1032                 relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1033                 crit_exit();
1034                 return EINVAL;
1035         }
1036
1037         if (ap->a_offset >= sndbuf_getsize(c->bufsoft)) {
1038                 relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1039                 crit_exit();
1040                 return EINVAL;
1041         }
1042
1043         if (!(c->flags & CHN_F_MAPPED))
1044                 c->flags |= CHN_F_MAPPED;
1045
1046         ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, ap->a_offset)));
1047         relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1048         crit_exit();
1049         ap->a_result = ret;
1050         return (0);
1051 }
1052
1053 int
1054 dsp_register(int unit, int channel)
1055 {
1056         dev_ops_add(&dsp_ops, 
1057                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1058         dev_ops_add(&dsp_ops, 
1059                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1060         dev_ops_add(&dsp_ops, 
1061                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1062         dev_ops_add(&dsp_ops, 
1063                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1064         make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSP, channel),
1065                  UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
1066         make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
1067                  UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
1068         make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
1069                  UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
1070
1071         return 0;
1072 }
1073
1074 int
1075 dsp_registerrec(int unit, int channel)
1076 {
1077         dev_ops_add(&dsp_ops, 
1078                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1079         make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
1080                  UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
1081
1082         return 0;
1083 }
1084
1085 int
1086 dsp_unregister(int unit, int channel)
1087 {
1088         dev_ops_remove(&dsp_ops, 
1089                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1090         dev_ops_remove(&dsp_ops, 
1091                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1092         dev_ops_remove(&dsp_ops, 
1093                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1094         dev_ops_remove(&dsp_ops, 
1095                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1096         return 0;
1097 }
1098
1099 int
1100 dsp_unregisterrec(int unit, int channel)
1101 {
1102         dev_ops_remove(&dsp_ops, 
1103                 PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1104         return 0;
1105 }
1106
1107 #ifdef USING_DEVFS
1108 static void
1109 dsp_clone(void *arg, char *name, int namelen, cdev_t *dev)
1110 {
1111         cdev_t pdev;
1112         int i, cont, unit, devtype;
1113         int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1114         char *devnames[3] = {"dsp", "dspW", "audio"};
1115
1116         if (*dev != NOCDEV)
1117                 return;
1118         if (pcm_devclass == NULL)
1119                 return;
1120
1121         devtype = 0;
1122         unit = -1;
1123         for (i = 0; (i < 3) && (unit == -1); i++) {
1124                 devtype = devtypes[i];
1125                 if (strcmp(name, devnames[i]) == 0) {
1126                         unit = snd_unit;
1127                 } else {
1128                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1129                                 unit = -1;
1130                 }
1131         }
1132         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1133                 return;
1134
1135         cont = 1;
1136         for (i = 0; cont; i++) {
1137                 pdev = make_adhoc_dev(&dsp_ops, PCMMKMINOR(unit, devtype, i));
1138                 if (pdev->si_flags & SI_NAMED) {
1139                         if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1140                                 *dev = pdev;
1141                                 return;
1142                         }
1143                 } else {
1144                         cont = 0;
1145                 }
1146         }
1147 }
1148
1149 static void
1150 dsp_sysinit(void *p)
1151 {
1152         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1153 }
1154
1155 static void
1156 dsp_sysuninit(void *p)
1157 {
1158         if (dsp_ehtag != NULL)
1159                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1160 }
1161
1162 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1163 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1164 #endif
1165
1166