]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/pcm/dsp.c
The SNDCTL_DSP_NONBLOCK ioctl doesn't take an argument, from
[FreeBSD/FreeBSD.git] / sys / dev / sound / pcm / dsp.c
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/param.h>
28 #include <sys/queue.h>
29
30 #include <dev/sound/pcm/sound.h>
31
32 SND_DECLARE_FILE("$FreeBSD$");
33
34 #define OLDPCM_IOCTL
35
36 static d_open_t dsp_open;
37 static d_close_t dsp_close;
38 static d_read_t dsp_read;
39 static d_write_t dsp_write;
40 static d_ioctl_t dsp_ioctl;
41 static d_poll_t dsp_poll;
42 static d_mmap_t dsp_mmap;
43
44 struct cdevsw dsp_cdevsw = {
45         .d_version =    D_VERSION,
46         .d_flags =      D_NEEDGIANT,
47         .d_open =       dsp_open,
48         .d_close =      dsp_close,
49         .d_read =       dsp_read,
50         .d_write =      dsp_write,
51         .d_ioctl =      dsp_ioctl,
52         .d_poll =       dsp_poll,
53         .d_mmap =       dsp_mmap,
54         .d_name =       "dsp",
55 };
56
57 #ifdef USING_DEVFS
58 static eventhandler_tag dsp_ehtag;
59 #endif
60
61 static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
62 static int dsp_oss_syncstart(int sg_id);
63 static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
64 #ifdef OSSV4_EXPERIMENT
65 static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
66 static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
67 static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
68 static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
69 static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
70 static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
71 static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
72 static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
73 #endif
74
75 static struct snddev_info *
76 dsp_get_info(struct cdev *dev)
77 {
78         struct snddev_info *d;
79         int unit;
80
81         unit = PCMUNIT(dev);
82         if (unit >= devclass_get_maxunit(pcm_devclass))
83                 return NULL;
84         d = devclass_get_softc(pcm_devclass, unit);
85
86         return d;
87 }
88
89 static u_int32_t
90 dsp_get_flags(struct cdev *dev)
91 {
92         device_t bdev;
93         int unit;
94
95         unit = PCMUNIT(dev);
96         if (unit >= devclass_get_maxunit(pcm_devclass))
97                 return 0xffffffff;
98         bdev = devclass_get_device(pcm_devclass, unit);
99
100         return pcm_getflags(bdev);
101 }
102
103 static void
104 dsp_set_flags(struct cdev *dev, u_int32_t flags)
105 {
106         device_t bdev;
107         int unit;
108
109         unit = PCMUNIT(dev);
110         if (unit >= devclass_get_maxunit(pcm_devclass))
111                 return;
112         bdev = devclass_get_device(pcm_devclass, unit);
113
114         pcm_setflags(bdev, flags);
115 }
116
117 /*
118  * return the channels associated with an open device instance.
119  * set the priority if the device is simplex and one direction (only) is
120  * specified.
121  * lock channels specified.
122  */
123 static int
124 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
125 {
126         struct snddev_info *d;
127         u_int32_t flags;
128
129         flags = dsp_get_flags(dev);
130         d = dsp_get_info(dev);
131         pcm_inprog(d, 1);
132         pcm_lock(d);
133         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
134                 ("getchns: read and write both prioritised"));
135
136         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
137                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
138                 dsp_set_flags(dev, flags);
139         }
140
141         *rdch = dev->si_drv1;
142         *wrch = dev->si_drv2;
143         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
144                 if (prio) {
145                         if (*rdch && flags & SD_F_PRIO_WR) {
146                                 dev->si_drv1 = NULL;
147                                 *rdch = pcm_getfakechan(d);
148                         } else if (*wrch && flags & SD_F_PRIO_RD) {
149                                 dev->si_drv2 = NULL;
150                                 *wrch = pcm_getfakechan(d);
151                         }
152                 }
153
154                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
155         }
156         pcm_unlock(d);
157
158         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
159                 CHN_LOCK(*rdch);
160         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
161                 CHN_LOCK(*wrch);
162
163         return 0;
164 }
165
166 /* unlock specified channels */
167 static void
168 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
169 {
170         struct snddev_info *d;
171
172         d = dsp_get_info(dev);
173         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
174                 CHN_UNLOCK(wrch);
175         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
176                 CHN_UNLOCK(rdch);
177         pcm_inprog(d, -1);
178 }
179
180 static int
181 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
182 {
183         struct pcm_channel *rdch, *wrch;
184         struct snddev_info *d;
185         u_int32_t fmt;
186         int devtype;
187         int error;
188         int chnum;
189
190         if (i_dev == NULL || td == NULL)
191                 return ENODEV;
192
193         if ((flags & (FREAD | FWRITE)) == 0)
194                 return EINVAL;
195
196         d = dsp_get_info(i_dev);
197         devtype = PCMDEV(i_dev);
198         chnum = -1;
199
200         /* decide default format */
201         switch (devtype) {
202         case SND_DEV_DSP16:
203                 fmt = AFMT_S16_LE;
204                 break;
205
206         case SND_DEV_DSP:
207                 fmt = AFMT_U8;
208                 break;
209
210         case SND_DEV_AUDIO:
211                 fmt = AFMT_MU_LAW;
212                 break;
213
214         case SND_DEV_NORESET:
215                 fmt = 0;
216                 break;
217
218         case SND_DEV_DSPREC:
219                 fmt = AFMT_U8;
220                 if (flags & FWRITE)
221                         return EINVAL;
222                 chnum = PCMCHAN(i_dev);
223                 break;
224
225         default:
226                 panic("impossible devtype %d", devtype);
227         }
228
229         /* lock snddev so nobody else can monkey with it */
230         pcm_lock(d);
231
232         rdch = i_dev->si_drv1;
233         wrch = i_dev->si_drv2;
234
235         if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) &&
236                     (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) {
237                 /* simplex or not, better safe than sorry. */
238                 pcm_unlock(d);
239                 return EBUSY;
240         }
241
242         /*
243          * if we get here, the open request is valid- either:
244          *   * we were previously not open
245          *   * we were open for play xor record and the opener wants
246          *     the non-open direction
247          */
248         if (flags & FREAD) {
249                 /* open for read */
250                 pcm_unlock(d);
251                 error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum);
252                 if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE))
253                         error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1);
254
255                 if (error == 0 && (chn_reset(rdch, fmt) ||
256                                 (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))))
257                         error = ENODEV;
258
259                 if (error != 0) {
260                         if (rdch)
261                                 pcm_chnrelease(rdch);
262                         return error;
263                 }
264
265                 if (flags & O_NONBLOCK)
266                         rdch->flags |= CHN_F_NBIO;
267                 pcm_chnref(rdch, 1);
268                 CHN_UNLOCK(rdch);
269                 pcm_lock(d);
270         }
271
272         if (flags & FWRITE) {
273             /* open for write */
274             pcm_unlock(d);
275             error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum);
276             if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD))
277                 error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1);
278
279             if (error == 0 && (chn_reset(wrch, fmt) ||
280                         (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))))
281                 error = ENODEV;
282
283             if (error != 0) {
284                 if (wrch)
285                     pcm_chnrelease(wrch);
286                 if (rdch) {
287                     /*
288                      * Lock, deref and release previously created record channel
289                      */
290                     CHN_LOCK(rdch);
291                     pcm_chnref(rdch, -1);
292                     pcm_chnrelease(rdch);
293                 }
294
295                 return error;
296             }
297
298             if (flags & O_NONBLOCK)
299                 wrch->flags |= CHN_F_NBIO;
300             pcm_chnref(wrch, 1);
301             CHN_UNLOCK(wrch);
302             pcm_lock(d);
303         }
304
305         i_dev->si_drv1 = rdch;
306         i_dev->si_drv2 = wrch;
307
308         pcm_unlock(d);
309         return 0;
310 }
311
312 static int
313 dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
314 {
315         struct pcm_channel *rdch, *wrch;
316         struct snddev_info *d;
317         int refs, sg_ids[2];
318
319         d = dsp_get_info(i_dev);
320         pcm_lock(d);
321         rdch = i_dev->si_drv1;
322         wrch = i_dev->si_drv2;
323         pcm_unlock(d);
324
325         /*
326          * Free_unr() may sleep, so store released syncgroup IDs until after
327          * all locks are released.
328          */
329         sg_ids[0] = sg_ids[1] = 0;
330
331         if (rdch || wrch) {
332                 refs = 0;
333                 if (rdch) {
334                         /*
335                          * The channel itself need not be locked because:
336                          *   a)  Adding a channel to a syncgroup happens only in dsp_ioctl(),
337                          *       which cannot run concurrently to dsp_close().
338                          *   b)  The syncmember pointer (sm) is protected by the global
339                          *       syncgroup list lock.
340                          *   c)  A channel can't just disappear, invalidating pointers,
341                          *       unless it's closed/dereferenced first.
342                          */
343                         PCM_SG_LOCK();
344                         sg_ids[0] = chn_syncdestroy(rdch);
345                         PCM_SG_UNLOCK();
346
347                         CHN_LOCK(rdch);
348                         refs += pcm_chnref(rdch, -1);
349                         chn_abort(rdch); /* won't sleep */
350                         rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
351                         chn_reset(rdch, 0);
352                         pcm_chnrelease(rdch);
353                 }
354                 if (wrch) {
355                         /*
356                          * Please see block above.
357                          */
358                         PCM_SG_LOCK();
359                         sg_ids[1] = chn_syncdestroy(wrch);
360                         PCM_SG_UNLOCK();
361
362                         CHN_LOCK(wrch);
363                         refs += pcm_chnref(wrch, -1);
364                         /*
365                          * XXX: Maybe the right behaviour is to abort on non_block.
366                          * It seems that mplayer flushes the audio queue by quickly
367                          * closing and re-opening.  In FBSD, there's a long pause
368                          * while the audio queue flushes that I presume isn't there in
369                          * linux.
370                          */
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                 pcm_lock(d);
378                 if (rdch)
379                         i_dev->si_drv1 = NULL;
380                 if (wrch)
381                         i_dev->si_drv2 = NULL;
382                 /*
383                  * If there are no more references, release the channels.
384                  */
385                 if (refs == 0 && i_dev->si_drv1 == NULL &&
386                             i_dev->si_drv2 == NULL) {
387                         if (pcm_getfakechan(d))
388                                 pcm_getfakechan(d)->flags = 0;
389                         /* What is this?!? */
390                         dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
391                 }
392                 pcm_unlock(d);
393         }
394
395
396         if (sg_ids[0])
397                 free_unr(pcmsg_unrhdr, sg_ids[0]);
398         if (sg_ids[1])
399                 free_unr(pcmsg_unrhdr, sg_ids[1]);
400
401         return 0;
402 }
403
404 static int
405 dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
406 {
407         struct pcm_channel *rdch, *wrch;
408         int ret;
409
410         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
411
412         KASSERT(rdch, ("dsp_read: nonexistant channel"));
413         KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
414
415         if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
416                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
417                 return EINVAL;
418         }
419         if (!(rdch->flags & CHN_F_RUNNING))
420                 rdch->flags |= CHN_F_RUNNING;
421         ret = chn_read(rdch, buf);
422         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
423
424         return ret;
425 }
426
427 static int
428 dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
429 {
430         struct pcm_channel *rdch, *wrch;
431         int ret;
432
433         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
434
435         KASSERT(wrch, ("dsp_write: nonexistant channel"));
436         KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
437
438         if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
439                 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
440                 return EINVAL;
441         }
442         if (!(wrch->flags & CHN_F_RUNNING))
443                 wrch->flags |= CHN_F_RUNNING;
444
445         /*
446          * Chn_write() must give up channel lock in order to copy bytes from
447          * userland, so up the "in progress" counter to make sure someone
448          * else doesn't come along and muss up the buffer.
449          */
450         ++wrch->inprog;
451         ret = chn_write(wrch, buf);
452         --wrch->inprog;
453         cv_signal(&wrch->cv);
454
455         relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
456
457         return ret;
458 }
459
460 static int
461 dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
462 {
463         struct pcm_channel *chn, *rdch, *wrch;
464         struct snddev_info *d;
465         int kill;
466         int ret = 0, *arg_i = (int *)arg, tmp;
467         int xcmd;
468
469         xcmd = 0;
470
471         /*
472          * this is an evil hack to allow broken apps to perform mixer ioctls
473          * on dsp devices.
474          */
475
476         d = dsp_get_info(i_dev);
477         if (IOCGROUP(cmd) == 'M') {
478                 /*
479                  * This is at least, a bug to bug compatible with OSS.
480                  */
481                 if (d->mixer_dev != NULL)
482                         return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
483                 else
484                         return EBADF;
485         }
486
487         /*
488          * Certain ioctls may be made on any type of device (audio, mixer,
489          * and MIDI).  Handle those special cases here.
490          */
491         if (IOCGROUP(cmd) == 'X') {
492                 switch(cmd) {
493                 case SNDCTL_SYSINFO:
494                         sound_oss_sysinfo((oss_sysinfo *)arg);
495                         break;
496                 case SNDCTL_AUDIOINFO:
497                         ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
498                         break;
499                 case SNDCTL_MIXERINFO:
500                         ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
501                         break;
502                 default:
503                         ret = EINVAL;
504                 }
505
506                 return ret;
507         }
508
509         getchns(i_dev, &rdch, &wrch, 0);
510
511         kill = 0;
512         if (wrch && (wrch->flags & CHN_F_DEAD))
513                 kill |= 1;
514         if (rdch && (rdch->flags & CHN_F_DEAD))
515                 kill |= 2;
516         if (kill == 3) {
517                 relchns(i_dev, rdch, wrch, 0);
518                 return EINVAL;
519         }
520         if (kill & 1)
521                 wrch = NULL;
522         if (kill & 2)
523                 rdch = NULL;
524         
525         switch(cmd) {
526 #ifdef OLDPCM_IOCTL
527         /*
528          * we start with the new ioctl interface.
529          */
530         case AIONWRITE: /* how many bytes can write ? */
531                 if (wrch) {
532                         CHN_LOCK(wrch);
533 /*
534                 if (wrch && wrch->bufhard.dl)
535                         while (chn_wrfeed(wrch) == 0);
536 */
537                         *arg_i = sndbuf_getfree(wrch->bufsoft);
538                         CHN_UNLOCK(wrch);
539                 } else {
540                         *arg_i = 0;
541                         ret = EINVAL;
542                 }
543                 break;
544
545         case AIOSSIZE:     /* set the current blocksize */
546                 {
547                         struct snd_size *p = (struct snd_size *)arg;
548
549                         p->play_size = 0;
550                         p->rec_size = 0;
551                         if (wrch) {
552                                 CHN_LOCK(wrch);
553                                 chn_setblocksize(wrch, 2, p->play_size);
554                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
555                                 CHN_UNLOCK(wrch);
556                         }
557                         if (rdch) {
558                                 CHN_LOCK(rdch);
559                                 chn_setblocksize(rdch, 2, p->rec_size);
560                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
561                                 CHN_UNLOCK(rdch);
562                         }
563                 }
564                 break;
565         case AIOGSIZE:  /* get the current blocksize */
566                 {
567                         struct snd_size *p = (struct snd_size *)arg;
568
569                         if (wrch) {
570                                 CHN_LOCK(wrch);
571                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
572                                 CHN_UNLOCK(wrch);
573                         }
574                         if (rdch) {
575                                 CHN_LOCK(rdch);
576                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
577                                 CHN_UNLOCK(rdch);
578                         }
579                 }
580                 break;
581
582         case AIOSFMT:
583         case AIOGFMT:
584                 {
585                         snd_chan_param *p = (snd_chan_param *)arg;
586
587                         if (cmd == AIOSFMT &&
588                             ((p->play_format != 0 && p->play_rate == 0) ||
589                             (p->rec_format != 0 && p->rec_rate == 0))) {
590                                 ret = EINVAL;
591                                 break;
592                         }
593                         if (wrch) {
594                                 CHN_LOCK(wrch);
595                                 if (cmd == AIOSFMT && p->play_format != 0) {
596                                         chn_setformat(wrch, p->play_format);
597                                         chn_setspeed(wrch, p->play_rate);
598                                 }
599                                 p->play_rate = wrch->speed;
600                                 p->play_format = wrch->format;
601                                 CHN_UNLOCK(wrch);
602                         } else {
603                                 p->play_rate = 0;
604                                 p->play_format = 0;
605                         }
606                         if (rdch) {
607                                 CHN_LOCK(rdch);
608                                 if (cmd == AIOSFMT && p->rec_format != 0) {
609                                         chn_setformat(rdch, p->rec_format);
610                                         chn_setspeed(rdch, p->rec_rate);
611                                 }
612                                 p->rec_rate = rdch->speed;
613                                 p->rec_format = rdch->format;
614                                 CHN_UNLOCK(rdch);
615                         } else {
616                                 p->rec_rate = 0;
617                                 p->rec_format = 0;
618                         }
619                 }
620                 break;
621
622         case AIOGCAP:     /* get capabilities */
623                 {
624                         snd_capabilities *p = (snd_capabilities *)arg;
625                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
626                         struct cdev *pdev;
627
628                         if (rdch) {
629                                 CHN_LOCK(rdch);
630                                 rcaps = chn_getcaps(rdch);
631                         }
632                         if (wrch) {
633                                 CHN_LOCK(wrch);
634                                 pcaps = chn_getcaps(wrch);
635                         }
636                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
637                                           pcaps? pcaps->minspeed : 0);
638                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
639                                           pcaps? pcaps->maxspeed : 1000000);
640                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
641                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
642                         /* XXX bad on sb16 */
643                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
644                                      (wrch? chn_getformats(wrch) : 0xffffffff);
645                         if (rdch && wrch)
646                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
647                         pdev = d->mixer_dev;
648                         p->mixers = 1; /* default: one mixer */
649                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
650                         p->left = p->right = 100;
651                         if (rdch)
652                                 CHN_UNLOCK(rdch);
653                         if (wrch)
654                                 CHN_UNLOCK(wrch);
655                 }
656                 break;
657
658         case AIOSTOP:
659                 if (*arg_i == AIOSYNC_PLAY && wrch) {
660                         CHN_LOCK(wrch);
661                         *arg_i = chn_abort(wrch);
662                         CHN_UNLOCK(wrch);
663                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
664                         CHN_LOCK(rdch);
665                         *arg_i = chn_abort(rdch);
666                         CHN_UNLOCK(rdch);
667                 } else {
668                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
669                         *arg_i = 0;
670                 }
671                 break;
672
673         case AIOSYNC:
674                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
675                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
676                 break;
677 #endif
678         /*
679          * here follow the standard ioctls (filio.h etc.)
680          */
681         case FIONREAD: /* get # bytes to read */
682                 if (rdch) {
683                         CHN_LOCK(rdch);
684 /*                      if (rdch && rdch->bufhard.dl)
685                                 while (chn_rdfeed(rdch) == 0);
686 */
687                         *arg_i = sndbuf_getready(rdch->bufsoft);
688                         CHN_UNLOCK(rdch);
689                 } else {
690                         *arg_i = 0;
691                         ret = EINVAL;
692                 }
693                 break;
694
695         case FIOASYNC: /*set/clear async i/o */
696                 DEB( printf("FIOASYNC\n") ; )
697                 break;
698
699         case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */
700         case FIONBIO: /* set/clear non-blocking i/o */
701                 if (rdch) {
702                         CHN_LOCK(rdch);
703                         if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
704                                 rdch->flags |= CHN_F_NBIO;
705                         else
706                                 rdch->flags &= ~CHN_F_NBIO;
707                         CHN_UNLOCK(rdch);
708                 }
709                 if (wrch) {
710                         CHN_LOCK(wrch);
711                         if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
712                                 wrch->flags |= CHN_F_NBIO;
713                         else
714                                 wrch->flags &= ~CHN_F_NBIO;
715                         CHN_UNLOCK(wrch);
716                 }
717                 break;
718
719         /*
720          * Finally, here is the linux-compatible ioctl interface
721          */
722 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
723         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
724         case SNDCTL_DSP_GETBLKSIZE:
725                 chn = wrch ? wrch : rdch;
726                 if (chn) {
727                         CHN_LOCK(chn);
728                         *arg_i = sndbuf_getblksz(chn->bufsoft);
729                         CHN_UNLOCK(chn);
730                 } else {
731                         *arg_i = 0;
732                         ret = EINVAL;
733                 }
734                 break ;
735
736         case SNDCTL_DSP_SETBLKSIZE:
737                 RANGE(*arg_i, 16, 65536);
738                 if (wrch) {
739                         CHN_LOCK(wrch);
740                         chn_setblocksize(wrch, 2, *arg_i);
741                         CHN_UNLOCK(wrch);
742                 }
743                 if (rdch) {
744                         CHN_LOCK(rdch);
745                         chn_setblocksize(rdch, 2, *arg_i);
746                         CHN_UNLOCK(rdch);
747                 }
748                 break;
749
750         case SNDCTL_DSP_RESET:
751                 DEB(printf("dsp reset\n"));
752                 if (wrch) {
753                         CHN_LOCK(wrch);
754                         chn_abort(wrch);
755                         chn_resetbuf(wrch);
756                         CHN_UNLOCK(wrch);
757                 }
758                 if (rdch) {
759                         CHN_LOCK(rdch);
760                         chn_abort(rdch);
761                         chn_resetbuf(rdch);
762                         CHN_UNLOCK(rdch);
763                 }
764                 break;
765
766         case SNDCTL_DSP_SYNC:
767                 DEB(printf("dsp sync\n"));
768                 /* chn_sync may sleep */
769                 if (wrch) {
770                         CHN_LOCK(wrch);
771                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
772                         CHN_UNLOCK(wrch);
773                 }
774                 break;
775
776         case SNDCTL_DSP_SPEED:
777                 /* chn_setspeed may sleep */
778                 tmp = 0;
779                 if (wrch) {
780                         CHN_LOCK(wrch);
781                         ret = chn_setspeed(wrch, *arg_i);
782                         tmp = wrch->speed;
783                         CHN_UNLOCK(wrch);
784                 }
785                 if (rdch && ret == 0) {
786                         CHN_LOCK(rdch);
787                         ret = chn_setspeed(rdch, *arg_i);
788                         if (tmp == 0)
789                                 tmp = rdch->speed;
790                         CHN_UNLOCK(rdch);
791                 }
792                 *arg_i = tmp;
793                 break;
794
795         case SOUND_PCM_READ_RATE:
796                 chn = wrch ? wrch : rdch;
797                 if (chn) {
798                         CHN_LOCK(chn);
799                         *arg_i = chn->speed;
800                         CHN_UNLOCK(chn);
801                 } else {
802                         *arg_i = 0;
803                         ret = EINVAL;
804                 }
805                 break;
806
807         case SNDCTL_DSP_STEREO:
808                 tmp = -1;
809                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
810                 if (wrch) {
811                         CHN_LOCK(wrch);
812                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
813                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
814                         CHN_UNLOCK(wrch);
815                 }
816                 if (rdch && ret == 0) {
817                         CHN_LOCK(rdch);
818                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
819                         if (tmp == -1)
820                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
821                         CHN_UNLOCK(rdch);
822                 }
823                 *arg_i = tmp;
824                 break;
825
826         case SOUND_PCM_WRITE_CHANNELS:
827 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
828                 if (*arg_i != 0) {
829                         tmp = 0;
830                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
831                         if (wrch) {
832                                 CHN_LOCK(wrch);
833                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
834                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
835                                 CHN_UNLOCK(wrch);
836                         }
837                         if (rdch && ret == 0) {
838                                 CHN_LOCK(rdch);
839                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
840                                 if (tmp == 0)
841                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
842                                 CHN_UNLOCK(rdch);
843                         }
844                         *arg_i = tmp;
845                 } else {
846                         chn = wrch ? wrch : rdch;
847                         CHN_LOCK(chn);
848                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
849                         CHN_UNLOCK(chn);
850                 }
851                 break;
852
853         case SOUND_PCM_READ_CHANNELS:
854                 chn = wrch ? wrch : rdch;
855                 if (chn) {
856                         CHN_LOCK(chn);
857                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
858                         CHN_UNLOCK(chn);
859                 } else {
860                         *arg_i = 0;
861                         ret = EINVAL;
862                 }
863                 break;
864
865         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
866                 chn = wrch ? wrch : rdch;
867                 if (chn) {
868                         CHN_LOCK(chn);
869                         *arg_i = chn_getformats(chn);
870                         CHN_UNLOCK(chn);
871                 } else {
872                         *arg_i = 0;
873                         ret = EINVAL;
874                 }
875                 break ;
876
877         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
878                 if ((*arg_i != AFMT_QUERY)) {
879                         tmp = 0;
880                         if (wrch) {
881                                 CHN_LOCK(wrch);
882                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
883                                 tmp = wrch->format & ~AFMT_STEREO;
884                                 CHN_UNLOCK(wrch);
885                         }
886                         if (rdch && ret == 0) {
887                                 CHN_LOCK(rdch);
888                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
889                                 if (tmp == 0)
890                                         tmp = rdch->format & ~AFMT_STEREO;
891                                 CHN_UNLOCK(rdch);
892                         }
893                         *arg_i = tmp;
894                 } else {
895                         chn = wrch ? wrch : rdch;
896                         CHN_LOCK(chn);
897                         *arg_i = chn->format & ~AFMT_STEREO;
898                         CHN_UNLOCK(chn);
899                 }
900                 break;
901
902         case SNDCTL_DSP_SETFRAGMENT:
903                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
904                 {
905                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
906                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
907                         u_int32_t fragsz;
908                         u_int32_t r_maxfrags, r_fragsz;
909
910                         RANGE(fragln, 4, 16);
911                         fragsz = 1 << fragln;
912
913                         if (maxfrags == 0)
914                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
915                         if (maxfrags < 2)
916                                 maxfrags = 2;
917                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
918                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
919
920                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
921                         if (rdch) {
922                                 CHN_LOCK(rdch);
923                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
924                                 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
925                                 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
926                                 CHN_UNLOCK(rdch);
927                         } else {
928                                 r_maxfrags = maxfrags;
929                                 r_fragsz = fragsz;
930                         }
931                         if (wrch && ret == 0) {
932                                 CHN_LOCK(wrch);
933                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
934                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
935                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
936                                 CHN_UNLOCK(wrch);
937                         } else { /* use whatever came from the read channel */
938                                 maxfrags = r_maxfrags;
939                                 fragsz = r_fragsz;
940                         }
941
942                         fragln = 0;
943                         while (fragsz > 1) {
944                                 fragln++;
945                                 fragsz >>= 1;
946                         }
947                         *arg_i = (maxfrags << 16) | fragln;
948                 }
949                 break;
950
951         case SNDCTL_DSP_GETISPACE:
952                 /* return the size of data available in the input queue */
953                 {
954                         audio_buf_info *a = (audio_buf_info *)arg;
955                         if (rdch) {
956                                 struct snd_dbuf *bs = rdch->bufsoft;
957
958                                 CHN_LOCK(rdch);
959                                 a->bytes = sndbuf_getready(bs);
960                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
961                                 a->fragstotal = sndbuf_getblkcnt(bs);
962                                 a->fragsize = sndbuf_getblksz(bs);
963                                 CHN_UNLOCK(rdch);
964                         }
965                 }
966                 break;
967
968         case SNDCTL_DSP_GETOSPACE:
969                 /* return space available in the output queue */
970                 {
971                         audio_buf_info *a = (audio_buf_info *)arg;
972                         if (wrch) {
973                                 struct snd_dbuf *bs = wrch->bufsoft;
974
975                                 CHN_LOCK(wrch);
976                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
977                                 a->bytes = sndbuf_getfree(bs);
978                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
979                                 a->fragstotal = sndbuf_getblkcnt(bs);
980                                 a->fragsize = sndbuf_getblksz(bs);
981                                 CHN_UNLOCK(wrch);
982                         }
983                 }
984                 break;
985
986         case SNDCTL_DSP_GETIPTR:
987                 {
988                         count_info *a = (count_info *)arg;
989                         if (rdch) {
990                                 struct snd_dbuf *bs = rdch->bufsoft;
991
992                                 CHN_LOCK(rdch);
993                                 /* XXX abusive DMA update: chn_rdupdate(rdch); */
994                                 a->bytes = sndbuf_gettotal(bs);
995                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
996                                 a->ptr = sndbuf_getreadyptr(bs);
997                                 rdch->blocks = sndbuf_getblocks(bs);
998                                 CHN_UNLOCK(rdch);
999                         } else
1000                                 ret = EINVAL;
1001                 }
1002                 break;
1003
1004         case SNDCTL_DSP_GETOPTR:
1005                 {
1006                         count_info *a = (count_info *)arg;
1007                         if (wrch) {
1008                                 struct snd_dbuf *bs = wrch->bufsoft;
1009
1010                                 CHN_LOCK(wrch);
1011                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
1012                                 a->bytes = sndbuf_gettotal(bs);
1013                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
1014                                 a->ptr = sndbuf_getreadyptr(bs);
1015                                 wrch->blocks = sndbuf_getblocks(bs);
1016                                 CHN_UNLOCK(wrch);
1017                         } else
1018                                 ret = EINVAL;
1019                 }
1020                 break;
1021
1022         case SNDCTL_DSP_GETCAPS:
1023                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
1024                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1025                         *arg_i |= DSP_CAP_DUPLEX;
1026                 break;
1027
1028         case SOUND_PCM_READ_BITS:
1029                 chn = wrch ? wrch : rdch;
1030                 if (chn) {
1031                         CHN_LOCK(chn);
1032                         if (chn->format & AFMT_8BIT)
1033                                 *arg_i = 8;
1034                         else if (chn->format & AFMT_16BIT)
1035                                 *arg_i = 16;
1036                         else if (chn->format & AFMT_24BIT)
1037                                 *arg_i = 24;
1038                         else if (chn->format & AFMT_32BIT)
1039                                 *arg_i = 32;
1040                         else
1041                                 ret = EINVAL;
1042                         CHN_UNLOCK(chn);
1043                 } else {
1044                         *arg_i = 0;
1045                         ret = EINVAL;
1046                 }
1047                 break;
1048
1049         case SNDCTL_DSP_SETTRIGGER:
1050                 if (rdch) {
1051                         CHN_LOCK(rdch);
1052                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1053                         if (*arg_i & PCM_ENABLE_INPUT)
1054                                 chn_start(rdch, 1);
1055                         else
1056                                 rdch->flags |= CHN_F_NOTRIGGER;
1057                         CHN_UNLOCK(rdch);
1058                 }
1059                 if (wrch) {
1060                         CHN_LOCK(wrch);
1061                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1062                         if (*arg_i & PCM_ENABLE_OUTPUT)
1063                                 chn_start(wrch, 1);
1064                         else
1065                                 wrch->flags |= CHN_F_NOTRIGGER;
1066                         CHN_UNLOCK(wrch);
1067                 }
1068                 break;
1069
1070         case SNDCTL_DSP_GETTRIGGER:
1071                 *arg_i = 0;
1072                 if (wrch) {
1073                         CHN_LOCK(wrch);
1074                         if (wrch->flags & CHN_F_TRIGGERED)
1075                                 *arg_i |= PCM_ENABLE_OUTPUT;
1076                         CHN_UNLOCK(wrch);
1077                 }
1078                 if (rdch) {
1079                         CHN_LOCK(rdch);
1080                         if (rdch->flags & CHN_F_TRIGGERED)
1081                                 *arg_i |= PCM_ENABLE_INPUT;
1082                         CHN_UNLOCK(rdch);
1083                 }
1084                 break;
1085
1086         case SNDCTL_DSP_GETODELAY:
1087                 if (wrch) {
1088                         struct snd_dbuf *b = wrch->bufhard;
1089                         struct snd_dbuf *bs = wrch->bufsoft;
1090
1091                         CHN_LOCK(wrch);
1092                         /* XXX abusive DMA update: chn_wrupdate(wrch); */
1093                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
1094                         CHN_UNLOCK(wrch);
1095                 } else
1096                         ret = EINVAL;
1097                 break;
1098
1099         case SNDCTL_DSP_POST:
1100                 if (wrch) {
1101                         CHN_LOCK(wrch);
1102                         wrch->flags &= ~CHN_F_NOTRIGGER;
1103                         chn_start(wrch, 1);
1104                         CHN_UNLOCK(wrch);
1105                 }
1106                 break;
1107
1108         case SNDCTL_DSP_SETDUPLEX:
1109                 /*
1110                  * switch to full-duplex mode if card is in half-duplex
1111                  * mode and is able to work in full-duplex mode
1112                  */
1113                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1114                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
1115                 break;
1116
1117         /*
1118          * The following four ioctls are simple wrappers around mixer_ioctl
1119          * with no further processing.  xcmd is short for "translated
1120          * command".
1121          */
1122         case SNDCTL_DSP_GETRECVOL:
1123                 if (xcmd == 0)
1124                         xcmd = SOUND_MIXER_READ_RECLEV;
1125                 /* FALLTHROUGH */
1126         case SNDCTL_DSP_SETRECVOL:
1127                 if (xcmd == 0)
1128                         xcmd = SOUND_MIXER_WRITE_RECLEV;
1129                 /* FALLTHROUGH */
1130         case SNDCTL_DSP_GETPLAYVOL:
1131                 if (xcmd == 0)
1132                         xcmd = SOUND_MIXER_READ_PCM;
1133                 /* FALLTHROUGH */
1134         case SNDCTL_DSP_SETPLAYVOL:
1135                 if (xcmd == 0)
1136                         xcmd = SOUND_MIXER_WRITE_PCM;
1137
1138                 if (d->mixer_dev != NULL)
1139                         ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td);
1140                 else
1141                         ret = ENOTSUP;
1142                 break;
1143
1144         case SNDCTL_DSP_GET_RECSRC_NAMES:
1145         case SNDCTL_DSP_GET_RECSRC:
1146         case SNDCTL_DSP_SET_RECSRC:
1147                 if (d->mixer_dev != NULL)
1148                         ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
1149                 else
1150                         ret = ENOTSUP;
1151                 break;
1152
1153         /*
1154          * The following 3 ioctls aren't very useful at the moment.  For
1155          * now, only a single channel is associated with a cdev (/dev/dspN
1156          * instance), so there's only a single output routing to use (i.e.,
1157          * the wrch bound to this cdev).
1158          */
1159         case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1160                 {
1161                         oss_mixer_enuminfo *ei;
1162                         ei = (oss_mixer_enuminfo *)arg;
1163                         ei->dev = 0;
1164                         ei->ctrl = 0;
1165                         ei->version = 0; /* static for now */
1166                         ei->strindex[0] = 0;
1167
1168                         if (wrch != NULL) {
1169                                 ei->nvalues = 1;
1170                                 strlcpy(ei->strings, wrch->name,
1171                                         sizeof(ei->strings));
1172                         } else {
1173                                 ei->nvalues = 0;
1174                                 ei->strings[0] = '\0';
1175                         }
1176                 }
1177                 break;
1178         case SNDCTL_DSP_GET_PLAYTGT:
1179         case SNDCTL_DSP_SET_PLAYTGT:    /* yes, they are the same for now */
1180                 /*
1181                  * Re: SET_PLAYTGT
1182                  *   OSSv4: "The value that was accepted by the device will
1183                  *   be returned back in the variable pointed by the
1184                  *   argument."
1185                  */
1186                 if (wrch != NULL)
1187                         *arg_i = 0;
1188                 else
1189                         ret = EINVAL;
1190                 break;
1191
1192         case SNDCTL_DSP_SILENCE:
1193         /*
1194          * Flush the software (pre-feed) buffer, but try to minimize playback
1195          * interruption.  (I.e., record unplayed samples with intent to
1196          * restore by SNDCTL_DSP_SKIP.) Intended for application "pause"
1197          * functionality.
1198          */
1199                 if (wrch == NULL)
1200                         ret = EINVAL;
1201                 else {
1202                         struct snd_dbuf *bs;
1203                         CHN_LOCK(wrch);
1204                         while (wrch->inprog != 0)
1205                                 cv_wait(&wrch->cv, wrch->lock);
1206                         bs = wrch->bufsoft;
1207                         if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) {
1208                                 bs->sl = sndbuf_getready(bs);
1209                                 sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs));
1210                                 sndbuf_fillsilence(bs);
1211                                 chn_start(wrch, 0);
1212                         }
1213                         CHN_UNLOCK(wrch);
1214                 }
1215                 break;
1216
1217         case SNDCTL_DSP_SKIP:
1218         /*
1219          * OSSv4 docs: "This ioctl call discards all unplayed samples in the
1220          * playback buffer by moving the current write position immediately
1221          * before the point where the device is currently reading the samples."
1222          */
1223                 if (wrch == NULL)
1224                         ret = EINVAL;
1225                 else {
1226                         struct snd_dbuf *bs;
1227                         CHN_LOCK(wrch);
1228                         while (wrch->inprog != 0)
1229                                 cv_wait(&wrch->cv, wrch->lock);
1230                         bs = wrch->bufsoft;
1231                         if ((bs->shadbuf != NULL) && (bs->sl > 0)) {
1232                                 sndbuf_softreset(bs);
1233                                 sndbuf_acquire(bs, bs->shadbuf, bs->sl);
1234                                 bs->sl = 0;
1235                                 chn_start(wrch, 0);
1236                         }
1237                         CHN_UNLOCK(wrch);
1238                 }
1239                 break;
1240
1241         case SNDCTL_DSP_CURRENT_OPTR:
1242         case SNDCTL_DSP_CURRENT_IPTR:
1243         /**
1244          * @note Changing formats resets the buffer counters, which differs
1245          *       from the 4Front drivers.  However, I don't expect this to be
1246          *       much of a problem.
1247          *
1248          * @note In a test where @c CURRENT_OPTR is called immediately after write
1249          *       returns, this driver is about 32K samples behind whereas
1250          *       4Front's is about 8K samples behind.  Should determine source
1251          *       of discrepancy, even if only out of curiosity.
1252          *
1253          * @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
1254          */
1255                 chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch;
1256                 if (chn == NULL) 
1257                         ret = EINVAL;
1258                 else {
1259                         struct snd_dbuf *bs;
1260                         /* int tmp; */
1261
1262                         oss_count_t *oc = (oss_count_t *)arg;
1263
1264                         CHN_LOCK(chn);
1265                         bs = chn->bufsoft;
1266 #if 0
1267                         tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
1268                         oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getbps(b);
1269                         oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(b);
1270 #else
1271                         oc->samples = sndbuf_gettotal(bs) / sndbuf_getbps(bs);
1272                         oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs);
1273 #endif
1274                         CHN_UNLOCK(chn);
1275                 }
1276                 break;
1277
1278         case SNDCTL_DSP_HALT_OUTPUT:
1279         case SNDCTL_DSP_HALT_INPUT:
1280                 chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch;
1281                 if (chn == NULL)
1282                         ret = EINVAL;
1283                 else {
1284                         CHN_LOCK(chn);
1285                         chn_abort(chn);
1286                         CHN_UNLOCK(chn);
1287                 }
1288                 break;
1289
1290         case SNDCTL_DSP_LOW_WATER:
1291         /*
1292          * Set the number of bytes required to attract attention by
1293          * select/poll.
1294          */
1295                 if (wrch != NULL) {
1296                         CHN_LOCK(wrch);
1297                         wrch->lw = (*arg_i > 1) ? *arg_i : 1;
1298                         CHN_UNLOCK(wrch);
1299                 }
1300                 if (rdch != NULL) {
1301                         CHN_LOCK(rdch);
1302                         rdch->lw = (*arg_i > 1) ? *arg_i : 1;
1303                         CHN_UNLOCK(rdch);
1304                 }
1305                 break;
1306
1307         case SNDCTL_DSP_GETERROR:
1308         /*
1309          * OSSv4 docs:  "All errors and counters will automatically be
1310          * cleared to zeroes after the call so each call will return only
1311          * the errors that occurred after the previous invocation. ... The
1312          * play_underruns and rec_overrun fields are the only usefull fields
1313          * returned by OSS 4.0."
1314          */
1315                 {
1316                         audio_errinfo *ei = (audio_errinfo *)arg;
1317
1318                         bzero((void *)ei, sizeof(*ei));
1319
1320                         if (wrch != NULL) {
1321                                 CHN_LOCK(wrch);
1322                                 ei->play_underruns = wrch->xruns;
1323                                 wrch->xruns = 0;
1324                                 CHN_UNLOCK(wrch);
1325                         }
1326                         if (rdch != NULL) {
1327                                 CHN_LOCK(rdch);
1328                                 ei->rec_overruns = rdch->xruns;
1329                                 rdch->xruns = 0;
1330                                 CHN_UNLOCK(rdch);
1331                         }
1332                 }
1333                 break;
1334
1335         case SNDCTL_DSP_SYNCGROUP:
1336                 ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
1337                 break;
1338
1339         case SNDCTL_DSP_SYNCSTART:
1340                 ret = dsp_oss_syncstart(*arg_i);
1341                 break;
1342
1343         case SNDCTL_DSP_POLICY:
1344                 ret = dsp_oss_policy(wrch, rdch, *arg_i);
1345                 break;
1346
1347 #ifdef  OSSV4_EXPERIMENT
1348         /*
1349          * XXX The following ioctls are not yet supported and just return
1350          * EINVAL.
1351          */
1352         case SNDCTL_DSP_GETOPEAKS:
1353         case SNDCTL_DSP_GETIPEAKS:
1354                 chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch;
1355                 if (chn == NULL)
1356                         ret = EINVAL;
1357                 else {
1358                         oss_peaks_t *op = (oss_peaks_t *)arg;
1359                         int lpeak, rpeak;
1360
1361                         CHN_LOCK(chn);
1362                         ret = chn_getpeaks(chn, &lpeak, &rpeak);
1363                         if (ret == -1)
1364                                 ret = EINVAL;
1365                         else {
1366                                 (*op)[0] = lpeak;
1367                                 (*op)[1] = rpeak;
1368                         }
1369                         CHN_UNLOCK(chn);
1370                 }
1371                 break;
1372
1373         case SNDCTL_DSP_COOKEDMODE:
1374                 ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
1375                 break;
1376         case SNDCTL_DSP_GET_CHNORDER:
1377                 ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg);
1378                 break;
1379         case SNDCTL_DSP_SET_CHNORDER:
1380                 ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg);
1381                 break;
1382         case SNDCTL_GETLABEL:
1383                 ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg);
1384                 break;
1385         case SNDCTL_SETLABEL:
1386                 ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg);
1387                 break;
1388         case SNDCTL_GETSONG:
1389                 ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg);
1390                 break;
1391         case SNDCTL_SETSONG:
1392                 ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg);
1393                 break;
1394         case SNDCTL_SETNAME:
1395                 ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
1396                 break;
1397 #if 0
1398         /**
1399          * @note The SNDCTL_CARDINFO ioctl was omitted per 4Front developer
1400          * documentation.  "The usability of this call is very limited. It's
1401          * provided only for completeness of the API. OSS API doesn't have
1402          * any concept of card. Any information returned by this ioctl calld
1403          * is reserved exclusively for the utility programs included in the
1404          * OSS package. Applications should not try to use for this
1405          * information in any ways."
1406          */
1407         case SNDCTL_CARDINFO:
1408                 ret = EINVAL;
1409                 break;
1410         /**
1411          * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
1412          * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
1413          * 4Front Technologies.
1414          */
1415         case SNDCTL_DSP_READCTL:
1416         case SNDCTL_DSP_WRITECTL:
1417                 ret = EINVAL;
1418                 break;
1419 #endif  /* !0 (explicitly omitted ioctls) */
1420
1421 #endif  /* !OSSV4_EXPERIMENT */
1422         case SNDCTL_DSP_MAPINBUF:
1423         case SNDCTL_DSP_MAPOUTBUF:
1424         case SNDCTL_DSP_SETSYNCRO:
1425                 /* undocumented */
1426
1427         case SNDCTL_DSP_SUBDIVIDE:
1428         case SOUND_PCM_WRITE_FILTER:
1429         case SOUND_PCM_READ_FILTER:
1430                 /* dunno what these do, don't sound important */
1431
1432         default:
1433                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
1434                 ret = EINVAL;
1435                 break;
1436         }
1437         relchns(i_dev, rdch, wrch, 0);
1438         return ret;
1439 }
1440
1441 static int
1442 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
1443 {
1444         struct pcm_channel *wrch = NULL, *rdch = NULL;
1445         int ret, e;
1446
1447         ret = 0;
1448         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1449
1450         if (wrch) {
1451                 e = (events & (POLLOUT | POLLWRNORM));
1452                 if (e)
1453                         ret |= chn_poll(wrch, e, td);
1454         }
1455         if (rdch) {
1456                 e = (events & (POLLIN | POLLRDNORM));
1457                 if (e)
1458                         ret |= chn_poll(rdch, e, td);
1459         }
1460         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1461
1462         return ret;
1463 }
1464
1465 static int
1466 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
1467 {
1468         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1469
1470         if (nprot & PROT_EXEC)
1471                 return -1;
1472
1473         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1474 #if 0
1475         /*
1476          * XXX the linux api uses the nprot to select read/write buffer
1477          * our vm system doesn't allow this, so force write buffer
1478          */
1479
1480         if (wrch && (nprot & PROT_WRITE)) {
1481                 c = wrch;
1482         } else if (rdch && (nprot & PROT_READ)) {
1483                 c = rdch;
1484         } else {
1485                 return -1;
1486         }
1487 #else
1488         c = wrch;
1489 #endif
1490
1491         if (c == NULL) {
1492                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1493                 return -1;
1494         }
1495
1496         if (offset >= sndbuf_getsize(c->bufsoft)) {
1497                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1498                 return -1;
1499         }
1500
1501         if (!(c->flags & CHN_F_MAPPED))
1502                 c->flags |= CHN_F_MAPPED;
1503
1504         *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
1505         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1506
1507         return 0;
1508 }
1509
1510 #ifdef USING_DEVFS
1511
1512 /*
1513  * Clone logic is this:
1514  * x E X = {dsp, dspW, audio}
1515  * x -> x${sysctl("hw.snd.unit")}
1516  * xN->
1517  *    for i N = 1 to channels of device N
1518  *      if xN.i isn't busy, return its dev_t
1519  */
1520 static void
1521 dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
1522     struct cdev **dev)
1523 {
1524         struct cdev *pdev;
1525         struct snddev_info *pcm_dev;
1526         struct snddev_channel *pcm_chan;
1527         int i, unit, devtype;
1528         static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1529         static char *devnames[3] = {"dsp", "dspW", "audio"};
1530
1531         if (*dev != NULL)
1532                 return;
1533         if (pcm_devclass == NULL)
1534                 return;
1535
1536         devtype = 0;
1537         unit = -1;
1538         for (i = 0; (i < 3) && (unit == -1); i++) {
1539                 devtype = devtypes[i];
1540                 if (strcmp(name, devnames[i]) == 0) {
1541                         unit = snd_unit;
1542                 } else {
1543                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1544                                 unit = -1;
1545                 }
1546         }
1547         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1548                 return;
1549
1550         pcm_dev = devclass_get_softc(pcm_devclass, unit);
1551
1552         if (pcm_dev == NULL)
1553                 return;
1554
1555         SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
1556
1557                 switch(devtype) {
1558                         case SND_DEV_DSP:
1559                                 pdev = pcm_chan->dsp_devt;
1560                                 break;
1561                         case SND_DEV_DSP16:
1562                                 pdev = pcm_chan->dspW_devt;
1563                                 break;
1564                         case SND_DEV_AUDIO:
1565                                 pdev = pcm_chan->audio_devt;
1566                                 break;
1567                         default:
1568                                 panic("Unknown devtype %d", devtype);
1569                 }
1570
1571                 if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1572                         *dev = pdev;
1573                         dev_ref(*dev);
1574                         return;
1575                 }
1576         }
1577 }
1578
1579 static void
1580 dsp_sysinit(void *p)
1581 {
1582         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1583 }
1584
1585 static void
1586 dsp_sysuninit(void *p)
1587 {
1588         if (dsp_ehtag != NULL)
1589                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1590 }
1591
1592 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1593 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1594 #endif
1595
1596 /**
1597  * @brief Handler for SNDCTL_AUDIOINFO.
1598  *
1599  * Gathers information about the audio device specified in ai->dev.  If
1600  * ai->dev == -1, then this function gathers information about the current
1601  * device.  If the call comes in on a non-audio device and ai->dev == -1,
1602  * return EINVAL.
1603  *
1604  * This routine is supposed to go practically straight to the hardware,
1605  * getting capabilities directly from the sound card driver, side-stepping
1606  * the intermediate channel interface.
1607  *
1608  * Note, however, that the usefulness of this command is significantly
1609  * decreased when requesting info about any device other than the one serving
1610  * the request. While each snddev_channel refers to a specific device node,
1611  * the converse is *not* true.  Currently, when a sound device node is opened,
1612  * the sound subsystem scans for an available audio channel (or channels, if
1613  * opened in read+write) and then assigns them to the si_drv[12] private
1614  * data fields.  As a result, any information returned linking a channel to
1615  * a specific character device isn't necessarily accurate.
1616  *
1617  * @note
1618  * Calling threads must not hold any snddev_info or pcm_channel locks.
1619  * 
1620  * @param dev           device on which the ioctl was issued
1621  * @param ai            ioctl request data container
1622  *
1623  * @retval 0            success
1624  * @retval EINVAL       ai->dev specifies an invalid device
1625  *
1626  * @todo Verify correctness of Doxygen tags.  ;)
1627  */
1628 int
1629 dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
1630 {
1631         struct snddev_channel *sce;
1632         struct pcmchan_caps *caps;
1633         struct pcm_channel *ch;
1634         struct snddev_info *d;
1635         struct cdev *t_cdev;
1636         uint32_t fmts;
1637         int i, nchan, ret, *rates, minch, maxch;
1638
1639         /*
1640          * If probing the device that received the ioctl, make sure it's a
1641          * DSP device.  (Users may use this ioctl with /dev/mixer and
1642          * /dev/midi.)
1643          */
1644         if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw))
1645                 return EINVAL;
1646
1647         ch = NULL;
1648         t_cdev = NULL;
1649         nchan = 0;
1650         ret = 0;
1651         
1652         /*
1653          * Search for the requested audio device (channel).  Start by
1654          * iterating over pcm devices.
1655          */ 
1656         for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
1657                 d = devclass_get_softc(pcm_devclass, i);
1658                 if (d == NULL)
1659                         continue;
1660
1661                 /* See the note in function docblock */
1662                 mtx_assert(d->lock, MA_NOTOWNED);
1663                 pcm_inprog(d, 1);
1664                 pcm_lock(d);
1665
1666                 SLIST_FOREACH(sce, &d->channels, link) {
1667                         ch = sce->channel;
1668                         mtx_assert(ch->lock, MA_NOTOWNED);
1669                         CHN_LOCK(ch);
1670                         if (ai->dev == -1) {
1671                                 if ((ch == i_dev->si_drv1) ||   /* record ch */
1672                                     (ch == i_dev->si_drv2)) {   /* playback ch */
1673                                         t_cdev = i_dev;
1674                                         goto dspfound;
1675                                 }
1676                         } else if (ai->dev == nchan) {
1677                                 t_cdev = sce->dsp_devt;
1678                                 goto dspfound;
1679                         }
1680                         CHN_UNLOCK(ch);
1681                         ++nchan;
1682                 }
1683
1684                 pcm_unlock(d);
1685                 pcm_inprog(d, -1);
1686         }
1687
1688         /* Exhausted the search -- nothing is locked, so return. */
1689         return EINVAL;
1690
1691 dspfound:
1692         /* Should've found the device, but something isn't right */
1693         if (t_cdev == NULL) {
1694                 ret = EINVAL;
1695                 goto out;
1696         }
1697
1698         /*
1699          * At this point, the following synchronization stuff has happened:
1700          *   - a specific PCM device is locked and its "in progress
1701          *     operations" counter has been incremented, so be sure to unlock
1702          *     and decrement when exiting;
1703          *   - a specific audio channel has been locked, so be sure to unlock
1704          *     when exiting;
1705          */
1706
1707         caps = chn_getcaps(ch);
1708
1709         /*
1710          * With all handles collected, zero out the user's container and
1711          * begin filling in its fields.
1712          */
1713         bzero((void *)ai, sizeof(oss_audioinfo));
1714
1715         ai->dev = nchan;
1716         strlcpy(ai->name, ch->name,  sizeof(ai->name));
1717
1718         if ((ch->flags & CHN_F_BUSY) == 0)
1719                 ai->busy = 0;
1720         else
1721                 ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
1722
1723         /**
1724          * @note
1725          * @c cmd - OSSv4 docs: "Only supported under Linux at this moment."
1726          *      Cop-out, I know, but I'll save running around in the process
1727          *      table for later.  Is there a risk of leaking information?
1728          */
1729         ai->pid = ch->pid;
1730         
1731         /*
1732          * These flags stolen from SNDCTL_DSP_GETCAPS handler.  Note, however,
1733          * that a single channel operates in only one direction, so
1734          * DSP_CAP_DUPLEX is out.
1735          */
1736         /**
1737          * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in
1738          *       pcmchan::caps?
1739          */
1740         ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
1741
1742         /*
1743          * Collect formats supported @b natively by the device.  Also
1744          * determine min/max channels.  (I.e., mono, stereo, or both?)
1745          *
1746          * If any channel is stereo, maxch = 2;
1747          * if all channels are stereo, minch = 2, too;
1748          * if any channel is mono, minch = 1;
1749          * and if all channels are mono, maxch = 1.
1750          */
1751         minch = 0;
1752         maxch = 0;
1753         fmts = 0;
1754         for (i = 0; caps->fmtlist[i]; i++) {
1755                 fmts |= caps->fmtlist[i];
1756                 if (caps->fmtlist[i] & AFMT_STEREO) {
1757                         minch = (minch == 0) ? 2 : minch;
1758                         maxch = 2;
1759                 } else {
1760                         minch = 1;
1761                         maxch = (maxch == 0) ? 1 : maxch;
1762                 }
1763         }
1764
1765         if (ch->direction == PCMDIR_PLAY)
1766                 ai->oformats = fmts;
1767         else
1768                 ai->iformats = fmts;
1769
1770         /**
1771          * @note
1772          * @c magic - OSSv4 docs: "Reserved for internal use by OSS."
1773          *
1774          * @par
1775          * @c card_number - OSSv4 docs: "Number of the sound card where this
1776          *      device belongs or -1 if this information is not available.
1777          *      Applications should normally not use this field for any
1778          *      purpose."
1779          */
1780         ai->card_number = -1;
1781         /**
1782          * @todo @c song_name - depends first on SNDCTL_[GS]ETSONG
1783          * @todo @c label - depends on SNDCTL_[GS]ETLABEL
1784          * @todo @c port_number - routing information?
1785          */
1786         ai->port_number = -1;
1787         ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
1788         /**
1789          * @note
1790          * @c real_device - OSSv4 docs:  "Obsolete."
1791          */
1792         ai->real_device = -1;
1793         strlcpy(ai->devnode, t_cdev->si_name, sizeof(ai->devnode));
1794         ai->enabled = device_is_attached(d->dev) ? 1 : 0;
1795         /**
1796          * @note
1797          * @c flags - OSSv4 docs: "Reserved for future use."
1798          *
1799          * @note
1800          * @c binding - OSSv4 docs: "Reserved for future use."
1801          *
1802          * @todo @c handle - haven't decided how to generate this yet; bus,
1803          *      vendor, device IDs?
1804          */
1805         ai->min_rate = caps->minspeed;
1806         ai->max_rate = caps->maxspeed;
1807
1808         ai->min_channels = minch;
1809         ai->max_channels = maxch;
1810
1811         ai->nrates = chn_getrates(ch, &rates);
1812         if (ai->nrates > MAX_SAMPLE_RATES)
1813                 ai->nrates = MAX_SAMPLE_RATES;
1814
1815         for (i = 0; i < ai->nrates; i++)
1816                 ai->rates[i] = rates[i];
1817
1818 out:
1819         CHN_UNLOCK(ch);
1820         pcm_unlock(d);
1821         pcm_inprog(d, -1);
1822
1823         return ret;
1824 }
1825
1826 /**
1827  * @brief Assigns a PCM channel to a sync group.
1828  *
1829  * Sync groups are used to enable audio operations on multiple devices
1830  * simultaneously.  They may be used with any number of devices and may
1831  * span across applications.  Devices are added to groups with
1832  * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the
1833  * SNDCTL_DSP_SYNCSTART ioctl.
1834  *
1835  * If the @c id field of the @c group parameter is set to zero, then a new
1836  * sync group is created.  Otherwise, wrch and rdch (if set) are added to
1837  * the group specified.
1838  *
1839  * @todo As far as memory allocation, should we assume that things are
1840  *       okay and allocate with M_WAITOK before acquiring channel locks,
1841  *       freeing later if not?
1842  *
1843  * @param wrch  output channel associated w/ device (if any)
1844  * @param rdch  input channel associated w/ device (if any)
1845  * @param group Sync group parameters
1846  *
1847  * @retval 0            success
1848  * @retval non-zero     error to be propagated upstream
1849  */
1850 static int
1851 dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group)
1852 {
1853         struct pcmchan_syncmember *smrd, *smwr;
1854         struct pcmchan_syncgroup *sg;
1855         int ret, sg_ids[3];
1856
1857         smrd = NULL;
1858         smwr = NULL;
1859         sg = NULL;
1860         ret = 0;
1861
1862         /*
1863          * Free_unr() may sleep, so store released syncgroup IDs until after
1864          * all locks are released.
1865          */
1866         sg_ids[0] = sg_ids[1] = sg_ids[2] = 0;
1867
1868         PCM_SG_LOCK();
1869
1870         /*
1871          * - Insert channel(s) into group's member list.
1872          * - Set CHN_F_NOTRIGGER on channel(s).
1873          * - Stop channel(s).  
1874          */
1875
1876         /*
1877          * If device's channels are already mapped to a group, unmap them.
1878          */
1879         if (wrch) {
1880                 CHN_LOCK(wrch);
1881                 sg_ids[0] = chn_syncdestroy(wrch);
1882         }
1883
1884         if (rdch) {
1885                 CHN_LOCK(rdch);
1886                 sg_ids[1] = chn_syncdestroy(rdch);
1887         }
1888
1889         /*
1890          * Verify that mode matches character device properites.
1891          *  - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
1892          *  - Bail if PCM_ENABLE_INPUT && rdch == NULL.
1893          */
1894         if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) ||
1895             ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) {
1896                 ret = EINVAL;
1897                 goto out;
1898         }
1899
1900         /*
1901          * An id of zero indicates the user wants to create a new
1902          * syncgroup.
1903          */
1904         if (group->id == 0) {
1905                 sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
1906                 if (sg != NULL) {
1907                         SLIST_INIT(&sg->members);
1908                         sg->id = alloc_unr(pcmsg_unrhdr);
1909
1910                         group->id = sg->id;
1911                         SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link);
1912                 } else
1913                         ret = ENOMEM;
1914         } else {
1915                 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
1916                         if (sg->id == group->id)
1917                                 break;
1918                 }
1919                 if (sg == NULL)
1920                         ret = EINVAL;
1921         }
1922
1923         /* Couldn't create or find a syncgroup.  Fail. */
1924         if (sg == NULL)
1925                 goto out;
1926
1927         /*
1928          * Allocate a syncmember, assign it and a channel together, and
1929          * insert into syncgroup.
1930          */
1931         if (group->mode & PCM_ENABLE_INPUT) {
1932                 smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
1933                 if (smrd == NULL) {
1934                         ret = ENOMEM;
1935                         goto out;
1936                 }
1937
1938                 SLIST_INSERT_HEAD(&sg->members, smrd, link);
1939                 smrd->parent = sg;
1940                 smrd->ch = rdch;
1941
1942                 chn_abort(rdch);
1943                 rdch->flags |= CHN_F_NOTRIGGER;
1944                 rdch->sm = smrd;
1945         }
1946
1947         if (group->mode & PCM_ENABLE_OUTPUT) {
1948                 smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
1949                 if (smwr == NULL) {
1950                         ret = ENOMEM;
1951                         goto out;
1952                 }
1953
1954                 SLIST_INSERT_HEAD(&sg->members, smwr, link);
1955                 smwr->parent = sg;
1956                 smwr->ch = wrch;
1957
1958                 chn_abort(wrch);
1959                 wrch->flags |= CHN_F_NOTRIGGER;
1960                 wrch->sm = smwr;
1961         }
1962
1963
1964 out:
1965         if (ret != 0) {
1966                 if (smrd != NULL)
1967                         free(smrd, M_DEVBUF);
1968                 if (smwr != NULL)
1969                         free(smwr, M_DEVBUF);
1970                 if ((sg != NULL) && SLIST_EMPTY(&sg->members)) {
1971                         sg_ids[2] = sg->id;
1972                         SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
1973                         free(sg, M_DEVBUF);
1974                 }
1975
1976                 if (wrch)
1977                         wrch->sm = NULL;
1978                 if (rdch)
1979                         rdch->sm = NULL;
1980         }
1981
1982         if (wrch)
1983                 CHN_UNLOCK(wrch);
1984         if (rdch)
1985                 CHN_UNLOCK(rdch);
1986
1987         PCM_SG_UNLOCK();
1988
1989         if (sg_ids[0])
1990                 free_unr(pcmsg_unrhdr, sg_ids[0]);
1991         if (sg_ids[1])
1992                 free_unr(pcmsg_unrhdr, sg_ids[1]);
1993         if (sg_ids[2])
1994                 free_unr(pcmsg_unrhdr, sg_ids[2]);
1995
1996         return ret;
1997 }
1998
1999 /**
2000  * @brief Launch a sync group into action
2001  *
2002  * Sync groups are established via SNDCTL_DSP_SYNCGROUP.  This function
2003  * iterates over all members, triggering them along the way.
2004  *
2005  * @note Caller must not hold any channel locks.
2006  *
2007  * @param sg_id sync group identifier
2008  *
2009  * @retval 0    success
2010  * @retval non-zero     error worthy of propagating upstream to user
2011  */
2012 static int
2013 dsp_oss_syncstart(int sg_id)
2014 {
2015         struct pcmchan_syncmember *sm, *sm_tmp;
2016         struct pcmchan_syncgroup *sg;
2017         struct pcm_channel *c;
2018         int ret, needlocks;
2019         
2020         /* Get the synclists lock */
2021         PCM_SG_LOCK();
2022
2023         do {
2024                 ret = 0;
2025                 needlocks = 0;
2026
2027                 /* Search for syncgroup by ID */
2028                 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
2029                         if (sg->id == sg_id)
2030                                 break;
2031                 }
2032
2033                 /* Return EINVAL if not found */
2034                 if (sg == NULL) {
2035                         ret = EINVAL;
2036                         break;
2037                 }
2038
2039                 /* Any removals resulting in an empty group should've handled this */
2040                 KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup"));
2041
2042                 /*
2043                  * Attempt to lock all member channels - if any are already
2044                  * locked, unlock those acquired, sleep for a bit, and try
2045                  * again.
2046                  */
2047                 SLIST_FOREACH(sm, &sg->members, link) {
2048                         if (CHN_TRYLOCK(sm->ch) == 0) {
2049                                 int timo = hz * 5/1000; 
2050                                 if (timo < 1)
2051                                         timo = 1;
2052
2053                                 /* Release all locked channels so far, retry */
2054                                 SLIST_FOREACH(sm_tmp, &sg->members, link) {
2055                                         /* sm is the member already locked */
2056                                         if (sm == sm_tmp)
2057                                                 break;
2058                                         CHN_UNLOCK(sm_tmp->ch);
2059                                 }
2060
2061                                 /** @todo Is PRIBIO correct/ */
2062                                 ret = msleep(sm, &snd_pcm_syncgroups_mtx, PRIBIO | PCATCH, "pcmsgrp", timo);
2063                                 if (ret == EINTR || ret == ERESTART)
2064                                         break;
2065
2066                                 needlocks = 1;
2067                                 ret = 0; /* Assumes ret == EWOULDBLOCK... */
2068                         }
2069                 }
2070         } while (needlocks && ret == 0);
2071
2072         /* Proceed only if no errors encountered. */
2073         if (ret == 0) {
2074                 /* Launch channels */
2075                 while((sm = SLIST_FIRST(&sg->members)) != NULL) {
2076                         SLIST_REMOVE_HEAD(&sg->members, link);
2077
2078                         c = sm->ch;
2079                         c->sm = NULL;
2080                         chn_start(c, 1);
2081                         c->flags &= ~CHN_F_NOTRIGGER;
2082                         CHN_UNLOCK(c);
2083
2084                         free(sm, M_DEVBUF);
2085                 }
2086
2087                 SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
2088                 free(sg, M_DEVBUF);
2089         }
2090
2091         PCM_SG_UNLOCK();
2092
2093         /*
2094          * Free_unr() may sleep, so be sure to give up the syncgroup lock
2095          * first.
2096          */
2097         if (ret == 0)
2098                 free_unr(pcmsg_unrhdr, sg_id);
2099
2100         return ret;
2101 }
2102
2103 /**
2104  * @brief Handler for SNDCTL_DSP_POLICY
2105  *
2106  * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment
2107  * size and count like with SNDCTL_DSP_SETFRAGMENT.  Instead of the user
2108  * specifying those two parameters, s/he simply selects a number from 0..10
2109  * which corresponds to a buffer size.  Smaller numbers request smaller
2110  * buffers with lower latencies (at greater overhead from more frequent
2111  * interrupts), while greater numbers behave in the opposite manner.
2112  *
2113  * The 4Front spec states that a value of 5 should be the default.  However,
2114  * this implementation deviates slightly by using a linear scale without
2115  * consulting drivers.  I.e., even though drivers may have different default
2116  * buffer sizes, a policy argument of 5 will have the same result across
2117  * all drivers.
2118  *
2119  * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for
2120  * more information.
2121  *
2122  * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to
2123  *       work with hardware drivers directly.
2124  *
2125  * @note PCM channel arguments must not be locked by caller.
2126  *
2127  * @param wrch  Pointer to opened playback channel (optional; may be NULL)
2128  * @param rdch  " recording channel (optional; may be NULL)
2129  * @param policy Integer from [0:10]
2130  *
2131  * @retval 0    constant (for now)
2132  */
2133 static int
2134 dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
2135 {
2136         int fragln, fragsz, maxfrags, ret;
2137
2138         /* Default: success */
2139         ret = 0;
2140
2141         /* Scale policy [0..10] to fragment size [2^4..2^16]. */
2142         fragln = policy;
2143         RANGE(fragln, 0, 10);
2144         fragln += 4;
2145         fragsz = 1 << fragln;
2146
2147         maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
2148
2149         if (rdch) {
2150                 CHN_LOCK(rdch);
2151                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
2152                 CHN_UNLOCK(rdch);
2153         }
2154
2155         if (wrch && ret == 0) {
2156                 CHN_LOCK(wrch);
2157                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
2158                 CHN_UNLOCK(wrch);
2159         }
2160
2161         return ret;
2162 }
2163
2164 #ifdef OSSV4_EXPERIMENT
2165 /**
2166  * @brief Enable or disable "cooked" mode
2167  *
2168  * This is a handler for @c SNDCTL_DSP_COOKEDMODE.  When in cooked mode, which
2169  * is the default, the sound system handles rate and format conversions
2170  * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only
2171  * operates with 44100Hz/16bit/signed samples).
2172  *
2173  * Disabling cooked mode is intended for applications wanting to mmap()
2174  * a sound card's buffer space directly, bypassing the FreeBSD 2-stage
2175  * feeder architecture, presumably to gain as much control over audio
2176  * hardware as possible.
2177  *
2178  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html
2179  * for more details.
2180  *
2181  * @note Currently, this function is just a stub that always returns EINVAL.
2182  *
2183  * @todo Figure out how to and actually implement this.
2184  *
2185  * @param wrch          playback channel (optional; may be NULL)
2186  * @param rdch          recording channel (optional; may be NULL)
2187  * @param enabled       0 = raw mode, 1 = cooked mode
2188  *
2189  * @retval EINVAL       Operation not yet supported.
2190  */
2191 static int
2192 dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled)
2193 {
2194         return EINVAL;
2195 }
2196
2197 /**
2198  * @brief Retrieve channel interleaving order
2199  *
2200  * This is the handler for @c SNDCTL_DSP_GET_CHNORDER.
2201  *
2202  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html
2203  * for more details.
2204  *
2205  * @note As the ioctl definition is still under construction, FreeBSD
2206  *       does not currently support SNDCTL_DSP_GET_CHNORDER.
2207  *
2208  * @param wrch  playback channel (optional; may be NULL)
2209  * @param rdch  recording channel (optional; may be NULL)
2210  * @param map   channel map (result will be stored there)
2211  *
2212  * @retval EINVAL       Operation not yet supported.
2213  */
2214 static int
2215 dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
2216 {
2217         return EINVAL;
2218 }
2219
2220 /**
2221  * @brief Specify channel interleaving order
2222  *
2223  * This is the handler for @c SNDCTL_DSP_SET_CHNORDER.
2224  *
2225  * @note As the ioctl definition is still under construction, FreeBSD
2226  *       does not currently support @c SNDCTL_DSP_SET_CHNORDER.
2227  *
2228  * @param wrch  playback channel (optional; may be NULL)
2229  * @param rdch  recording channel (optional; may be NULL)
2230  * @param map   channel map
2231  *
2232  * @retval EINVAL       Operation not yet supported.
2233  */
2234 static int
2235 dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
2236 {
2237         return EINVAL;
2238 }
2239
2240 /**
2241  * @brief Retrieve an audio device's label
2242  *
2243  * This is a handler for the @c SNDCTL_GETLABEL ioctl.
2244  *
2245  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
2246  * for more details.
2247  *
2248  * From Hannu@4Front:  "For example ossxmix (just like some HW mixer
2249  * consoles) can show variable "labels" for certain controls. By default
2250  * the application name (say quake) is shown as the label but
2251  * applications may change the labels themselves."
2252  *
2253  * @note As the ioctl definition is still under construction, FreeBSD
2254  *       does not currently support @c SNDCTL_GETLABEL.
2255  *
2256  * @param wrch  playback channel (optional; may be NULL)
2257  * @param rdch  recording channel (optional; may be NULL)
2258  * @param label label gets copied here
2259  *
2260  * @retval EINVAL       Operation not yet supported.
2261  */
2262 static int
2263 dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
2264 {
2265         return EINVAL;
2266 }
2267
2268 /**
2269  * @brief Specify an audio device's label
2270  *
2271  * This is a handler for the @c SNDCTL_SETLABEL ioctl.  Please see the
2272  * comments for @c dsp_oss_getlabel immediately above.
2273  *
2274  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
2275  * for more details.
2276  *
2277  * @note As the ioctl definition is still under construction, FreeBSD
2278  *       does not currently support SNDCTL_SETLABEL.
2279  *
2280  * @param wrch  playback channel (optional; may be NULL)
2281  * @param rdch  recording channel (optional; may be NULL)
2282  * @param label label gets copied from here
2283  *
2284  * @retval EINVAL       Operation not yet supported.
2285  */
2286 static int
2287 dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
2288 {
2289         return EINVAL;
2290 }
2291
2292 /**
2293  * @brief Retrieve name of currently played song
2294  *
2295  * This is a handler for the @c SNDCTL_GETSONG ioctl.  Audio players could
2296  * tell the system the name of the currently playing song, which would be
2297  * visible in @c /dev/sndstat.
2298  *
2299  * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html
2300  * for more details.
2301  *
2302  * @note As the ioctl definition is still under construction, FreeBSD
2303  *       does not currently support SNDCTL_GETSONG.
2304  *
2305  * @param wrch  playback channel (optional; may be NULL)
2306  * @param rdch  recording channel (optional; may be NULL)
2307  * @param song  song name gets copied here
2308  *
2309  * @retval EINVAL       Operation not yet supported.
2310  */
2311 static int
2312 dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
2313 {
2314         return EINVAL;
2315 }
2316
2317 /**
2318  * @brief Retrieve name of currently played song
2319  *
2320  * This is a handler for the @c SNDCTL_SETSONG ioctl.  Audio players could
2321  * tell the system the name of the currently playing song, which would be
2322  * visible in @c /dev/sndstat.
2323  *
2324  * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html
2325  * for more details.
2326  *
2327  * @note As the ioctl definition is still under construction, FreeBSD
2328  *       does not currently support SNDCTL_SETSONG.
2329  *
2330  * @param wrch  playback channel (optional; may be NULL)
2331  * @param rdch  recording channel (optional; may be NULL)
2332  * @param song  song name gets copied from here
2333  *
2334  * @retval EINVAL       Operation not yet supported.
2335  */
2336 static int
2337 dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
2338 {
2339         return EINVAL;
2340 }
2341
2342 /**
2343  * @brief Rename a device
2344  *
2345  * This is a handler for the @c SNDCTL_SETNAME ioctl.
2346  *
2347  * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for
2348  * more details.
2349  *
2350  * From Hannu@4Front:  "This call is used to change the device name
2351  * reported in /dev/sndstat and ossinfo. So instead of  using some generic
2352  * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull
2353  * name depending on the current context (for example 'OSS virtual wave table
2354  * synth' or 'VoIP link to London')."
2355  *
2356  * @note As the ioctl definition is still under construction, FreeBSD
2357  *       does not currently support SNDCTL_SETNAME.
2358  *
2359  * @param wrch  playback channel (optional; may be NULL)
2360  * @param rdch  recording channel (optional; may be NULL)
2361  * @param name  new device name gets copied from here
2362  *
2363  * @retval EINVAL       Operation not yet supported.
2364  */
2365 static int
2366 dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name)
2367 {
2368         return EINVAL;
2369 }
2370 #endif  /* !OSSV4_EXPERIMENT */