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