File:  [DragonFly] / src / sys / dev / sound / pcm / dsp.c
Revision 1.5: download - view: text, annotated - select for diffs
Thu May 13 23:49:21 2004 UTC (10 years, 2 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

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