File:  [DragonFly] / src / sys / dev / sound / pcm / mixer.c
Revision 1.5: download - view: text, annotated - select for diffs
Thu May 13 23:49:21 2004 UTC (10 years, 3 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/mixer.c,v 1.4.2.8 2002/04/22 15:49:36 cg Exp $
   27:  * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.5 2004/05/13 23:49:21 dillon Exp $
   28:  */
   29: 
   30: #include <dev/sound/pcm/sound.h>
   31: 
   32: #include "mixer_if.h"
   33: 
   34: SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.5 2004/05/13 23:49:21 dillon Exp $");
   35: 
   36: MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
   37: 
   38: #define MIXER_NAMELEN	16
   39: struct snd_mixer {
   40: 	KOBJ_FIELDS;
   41: 	const char *type;
   42: 	void *devinfo;
   43: 	int busy;
   44: 	int hwvol_muted;
   45: 	int hwvol_mixer;
   46: 	int hwvol_step;
   47: 	u_int32_t hwvol_mute_level;
   48: 	u_int32_t devs;
   49: 	u_int32_t recdevs;
   50: 	u_int32_t recsrc;
   51: 	u_int16_t level[32];
   52: 	char name[MIXER_NAMELEN];
   53: 	void *lock;
   54: };
   55: 
   56: static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
   57: 	[SOUND_MIXER_VOLUME]	= 75,
   58: 	[SOUND_MIXER_BASS]	= 50,
   59: 	[SOUND_MIXER_TREBLE]	= 50,
   60: 	[SOUND_MIXER_SYNTH]	= 75,
   61: 	[SOUND_MIXER_PCM]	= 75,
   62: 	[SOUND_MIXER_SPEAKER]	= 75,
   63: 	[SOUND_MIXER_LINE]	= 75,
   64: 	[SOUND_MIXER_MIC] 	= 0,
   65: 	[SOUND_MIXER_CD]	= 75,
   66: 	[SOUND_MIXER_LINE1]	= 75,
   67: 	[SOUND_MIXER_VIDEO]	= 75,
   68: 	[SOUND_MIXER_RECLEV]	= 0,
   69: 	[SOUND_MIXER_OGAIN]	= 50,
   70: 	[SOUND_MIXER_MONITOR]	= 75,
   71: };
   72: 
   73: static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
   74: 
   75: static d_open_t mixer_open;
   76: static d_close_t mixer_close;
   77: 
   78: static struct cdevsw mixer_cdevsw = {
   79: 	/* name */	"mixer",
   80: 	/* maj */	SND_CDEV_MAJOR,
   81: 	/* flags */	0,
   82: 	/* port */	NULL,
   83: 	/* clone */	NULL,
   84: 
   85: 	/* open */	mixer_open,
   86: 	/* close */	mixer_close,
   87: 	/* read */	noread,
   88: 	/* write */	nowrite,
   89: 	/* ioctl */	mixer_ioctl,
   90: 	/* poll */	nopoll,
   91: 	/* mmap */	nommap,
   92: 	/* strategy */	nostrategy,
   93: 	/* dump */	nodump,
   94: 	/* psize */	nopsize
   95: };
   96: 
   97: #ifdef USING_DEVFS
   98: static eventhandler_tag mixer_ehtag;
   99: #endif
  100: 
  101: static dev_t
  102: mixer_get_devt(device_t dev)
  103: {
  104: 	dev_t pdev;
  105: 	int unit;
  106: 
  107: 	unit = device_get_unit(dev);
  108: 	pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
  109: 
  110: 	return pdev;
  111: }
  112: 
  113: #ifdef SND_DYNSYSCTL
  114: static int
  115: mixer_lookup(char *devname)
  116: {
  117: 	int i;
  118: 
  119: 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  120: 		if (strncmp(devname, snd_mixernames[i],
  121: 		    strlen(snd_mixernames[i])) == 0)
  122: 			return i;
  123: 	return -1;
  124: }
  125: #endif
  126: 
  127: static int
  128: mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
  129: {
  130: 	unsigned l, r;
  131: 	int v;
  132: 
  133: 	if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
  134: 		return -1;
  135: 
  136: 	l = min((lev & 0x00ff), 100);
  137: 	r = min(((lev & 0xff00) >> 8), 100);
  138: 
  139: 	v = MIXER_SET(mixer, dev, l, r);
  140: 	if (v < 0)
  141: 		return -1;
  142: 
  143: 	mixer->level[dev] = l | (r << 8);
  144: 	return 0;
  145: }
  146: 
  147: static int
  148: mixer_get(struct snd_mixer *mixer, int dev)
  149: {
  150: 	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
  151: 		return mixer->level[dev];
  152: 	else return -1;
  153: }
  154: 
  155: static int
  156: mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
  157: {
  158: 	src &= mixer->recdevs;
  159: 	if (src == 0)
  160: 		src = SOUND_MASK_MIC;
  161: 	mixer->recsrc = MIXER_SETRECSRC(mixer, src);
  162: 	return 0;
  163: }
  164: 
  165: static int
  166: mixer_getrecsrc(struct snd_mixer *mixer)
  167: {
  168: 	return mixer->recsrc;
  169: }
  170: 
  171: void
  172: mix_setdevs(struct snd_mixer *m, u_int32_t v)
  173: {
  174: 	m->devs = v;
  175: }
  176: 
  177: void
  178: mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
  179: {
  180: 	m->recdevs = v;
  181: }
  182: 
  183: u_int32_t
  184: mix_getdevs(struct snd_mixer *m)
  185: {
  186: 	return m->devs;
  187: }
  188: 
  189: u_int32_t
  190: mix_getrecdevs(struct snd_mixer *m)
  191: {
  192: 	return m->recdevs;
  193: }
  194: 
  195: void *
  196: mix_getdevinfo(struct snd_mixer *m)
  197: {
  198: 	return m->devinfo;
  199: }
  200: 
  201: int
  202: mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
  203: {
  204: 	struct snd_mixer *m;
  205: 	u_int16_t v;
  206: 	dev_t pdev;
  207: 	int i, unit;
  208: 
  209: 	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
  210: 	snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
  211: 	m->lock = snd_mtxcreate(m->name, "pcm mixer");
  212: 	m->type = cls->name;
  213: 	m->devinfo = devinfo;
  214: 	m->busy = 0;
  215: 
  216: 	if (MIXER_INIT(m))
  217: 		goto bad;
  218: 
  219: 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  220: 		v = snd_mixerdefaults[i];
  221: 		mixer_set(m, i, v | (v << 8));
  222: 	}
  223: 
  224: 	mixer_setrecsrc(m, SOUND_MASK_MIC);
  225: 
  226: 	unit = device_get_unit(dev);
  227: 	pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
  228: 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
  229: 	pdev->si_drv1 = m;
  230: 
  231: 	return 0;
  232: 
  233: bad:
  234: 	snd_mtxlock(m->lock);
  235: 	snd_mtxfree(m->lock);
  236: 	kobj_delete((kobj_t)m, M_MIXER);
  237: 	return -1;
  238: }
  239: 
  240: int
  241: mixer_uninit(device_t dev)
  242: {
  243: 	int i;
  244: 	struct snd_mixer *m;
  245: 	dev_t pdev;
  246: 
  247: 	pdev = mixer_get_devt(dev);
  248: 	m = pdev->si_drv1;
  249: 	snd_mtxlock(m->lock);
  250: 
  251: 	if (m->busy) {
  252: 		snd_mtxunlock(m->lock);
  253: 		return EBUSY;
  254: 	}
  255: 
  256: 	pdev->si_drv1 = NULL;
  257: 	destroy_dev(pdev);
  258: 
  259: 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  260: 		mixer_set(m, i, 0);
  261: 
  262: 	mixer_setrecsrc(m, SOUND_MASK_MIC);
  263: 
  264: 	MIXER_UNINIT(m);
  265: 
  266: 	snd_mtxfree(m->lock);
  267: 	kobj_delete((kobj_t)m, M_MIXER);
  268: 
  269: 	return 0;
  270: }
  271: 
  272: int
  273: mixer_reinit(device_t dev)
  274: {
  275: 	struct snd_mixer *m;
  276: 	dev_t pdev;
  277: 	int i;
  278: 
  279: 	pdev = mixer_get_devt(dev);
  280: 	m = pdev->si_drv1;
  281: 	snd_mtxlock(m->lock);
  282: 
  283: 	i = MIXER_REINIT(m);
  284: 	if (i) {
  285: 		snd_mtxunlock(m->lock);
  286: 		return i;
  287: 	}
  288: 
  289: 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  290: 		mixer_set(m, i, m->level[i]);
  291: 
  292: 	mixer_setrecsrc(m, m->recsrc);
  293: 	snd_mtxunlock(m->lock);
  294: 
  295: 	return 0;
  296: }
  297: 
  298: #ifdef SND_DYNSYSCTL
  299: static int
  300: sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
  301: {
  302: 	char devname[32];
  303: 	int error, dev;
  304: 	struct snd_mixer *m;
  305: 
  306: 	m = oidp->oid_arg1;
  307: 	snd_mtxlock(m->lock);
  308: 	strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
  309: 	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
  310: 	if (error == 0 && req->newptr != NULL) {
  311: 		dev = mixer_lookup(devname);
  312: 		if (dev == -1) {
  313: 			snd_mtxunlock(m->lock);
  314: 			return EINVAL;
  315: 		}
  316: 		else if (dev != m->hwvol_mixer) {
  317: 			m->hwvol_mixer = dev;
  318: 			m->hwvol_muted = 0;
  319: 		}
  320: 	}
  321: 	snd_mtxunlock(m->lock);
  322: 	return error;
  323: }
  324: #endif
  325: 
  326: int
  327: mixer_hwvol_init(device_t dev)
  328: {
  329: 	struct snd_mixer *m;
  330: 	dev_t pdev;
  331: 
  332: 	pdev = mixer_get_devt(dev);
  333: 	m = pdev->si_drv1;
  334: 	snd_mtxlock(m->lock);
  335: 
  336: 	m->hwvol_mixer = SOUND_MIXER_VOLUME;
  337: 	m->hwvol_step = 5;
  338: #ifdef SND_DYNSYSCTL
  339: 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  340:             OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
  341: 	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  342:             OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
  343: 	    sysctl_hw_snd_hwvol_mixer, "A", "")
  344: #endif
  345: 	snd_mtxunlock(m->lock);
  346: 	return 0;
  347: }
  348: 
  349: void
  350: mixer_hwvol_mute(device_t dev)
  351: {
  352: 	struct snd_mixer *m;
  353: 	dev_t pdev;
  354: 
  355: 	pdev = mixer_get_devt(dev);
  356: 	m = pdev->si_drv1;
  357: 	snd_mtxlock(m->lock);
  358: 	if (m->hwvol_muted) {
  359: 		m->hwvol_muted = 0;
  360: 		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
  361: 	} else {
  362: 		m->hwvol_muted++;
  363: 		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
  364: 		mixer_set(m, m->hwvol_mixer, 0);
  365: 	}
  366: 	snd_mtxunlock(m->lock);
  367: }
  368: 
  369: void
  370: mixer_hwvol_step(device_t dev, int left_step, int right_step)
  371: {
  372: 	struct snd_mixer *m;
  373: 	int level, left, right;
  374: 	dev_t pdev;
  375: 
  376: 	pdev = mixer_get_devt(dev);
  377: 	m = pdev->si_drv1;
  378: 	snd_mtxlock(m->lock);
  379: 	if (m->hwvol_muted) {
  380: 		m->hwvol_muted = 0;
  381: 		level = m->hwvol_mute_level;
  382: 	} else
  383: 		level = mixer_get(m, m->hwvol_mixer);
  384: 	if (level != -1) {
  385: 		left = level & 0xff;
  386: 		right = level >> 8;
  387: 		left += left_step * m->hwvol_step;
  388: 		if (left < 0)
  389: 			left = 0;
  390: 		right += right_step * m->hwvol_step;
  391: 		if (right < 0)
  392: 			right = 0;
  393: 		mixer_set(m, m->hwvol_mixer, left | right << 8);
  394: 	}
  395: 	snd_mtxunlock(m->lock);
  396: }
  397: 
  398: /* ----------------------------------------------------------------------- */
  399: 
  400: static int
  401: mixer_open(dev_t i_dev, int flags, int mode, struct thread *td)
  402: {
  403: 	struct snd_mixer *m;
  404: 	intrmask_t s;
  405: 
  406: 	m = i_dev->si_drv1;
  407: 	s = spltty();
  408: 	snd_mtxlock(m->lock);
  409: 
  410: 	m->busy++;
  411: 
  412: 	snd_mtxunlock(m->lock);
  413: 	splx(s);
  414: 	return 0;
  415: }
  416: 
  417: static int
  418: mixer_close(dev_t i_dev, int flags, int mode, struct thread *td)
  419: {
  420: 	struct snd_mixer *m;
  421: 	intrmask_t s;
  422: 
  423: 	m = i_dev->si_drv1;
  424: 	s = spltty();
  425: 	snd_mtxlock(m->lock);
  426: 
  427: 	if (!m->busy) {
  428: 		snd_mtxunlock(m->lock);
  429: 		splx(s);
  430: 		return EBADF;
  431: 	}
  432: 	m->busy--;
  433: 
  434: 	snd_mtxunlock(m->lock);
  435: 	splx(s);
  436: 	return 0;
  437: }
  438: 
  439: int
  440: mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
  441: {
  442: 	struct snd_mixer *m;
  443: 	intrmask_t s;
  444: 	int ret, *arg_i = (int *)arg;
  445: 	int v = -1, j = cmd & 0xff;
  446: 
  447: 	m = i_dev->si_drv1;
  448: 	if (!m->busy)
  449: 		return EBADF;
  450: 
  451: 	s = spltty();
  452: 	snd_mtxlock(m->lock);
  453: 	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
  454: 		if (j == SOUND_MIXER_RECSRC)
  455: 			ret = mixer_setrecsrc(m, *arg_i);
  456: 		else
  457: 			ret = mixer_set(m, j, *arg_i);
  458: 		snd_mtxunlock(m->lock);
  459: 		splx(s);
  460: 		return (ret == 0)? 0 : ENXIO;
  461: 	}
  462: 
  463:     	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
  464: 		switch (j) {
  465:     		case SOUND_MIXER_DEVMASK:
  466:     		case SOUND_MIXER_CAPS:
  467:     		case SOUND_MIXER_STEREODEVS:
  468: 			v = mix_getdevs(m);
  469: 			break;
  470: 
  471:     		case SOUND_MIXER_RECMASK:
  472: 			v = mix_getrecdevs(m);
  473: 			break;
  474: 
  475:     		case SOUND_MIXER_RECSRC:
  476: 			v = mixer_getrecsrc(m);
  477: 			break;
  478: 
  479: 		default:
  480: 			v = mixer_get(m, j);
  481: 		}
  482: 		*arg_i = v;
  483: 		snd_mtxunlock(m->lock);
  484: 		return (v != -1)? 0 : ENXIO;
  485: 	}
  486: 	snd_mtxunlock(m->lock);
  487: 	splx(s);
  488: 	return ENXIO;
  489: }
  490: 
  491: #ifdef USING_DEVFS
  492: static void
  493: mixer_clone(void *arg, char *name, int namelen, dev_t *dev)
  494: {
  495: 	dev_t pdev;
  496: 
  497: 	if (*dev != NODEV)
  498: 		return;
  499: 	if (strcmp(name, "mixer") == 0) {
  500: 		pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(snd_unit, SND_DEV_CTL, 0));
  501: 		if (pdev->si_flags & SI_NAMED)
  502: 			*dev = pdev;
  503: 	}
  504: }
  505: 
  506: static void
  507: mixer_sysinit(void *p)
  508: {
  509: 	mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
  510: }
  511: 
  512: static void
  513: mixer_sysuninit(void *p)
  514: {
  515: 	if (mixer_ehtag != NULL)
  516: 		EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
  517: }
  518: 
  519: SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
  520: SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
  521: #endif
  522: 
  523: