OpenSolaris_b135/cmd/audio/utilities/device_ctl.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1989-2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * This file contains routines to read and write the audio device state.
 */

#include <errno.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <libaudio_impl.h>
#include <audio_errno.h>
#include <audio_hdr.h>
#include <audio_device.h>


/*
 * Get device information structure.
 */
int
audio_getinfo(int fd, Audio_info *ip)
{
	if (ioctl(fd, AUDIO_GETINFO, (char *)ip) < 0) {
		return (AUDIO_UNIXERROR);
	} else {
		return (AUDIO_SUCCESS);
	}
}

/*
 * Set device information structure.
 * The calling routine should use AUDIO_INITINFO prior to setting new values.
 */
int
audio_setinfo(int fd, Audio_info *ip)
{
	if (ioctl(fd, AUDIO_SETINFO, (char *)ip) < 0) {
		return (AUDIO_UNIXERROR);
	} else {
		return (AUDIO_SUCCESS);
	}
}

/*
 * Return an Audio_hdr corresponding to the encoding configuration
 * of the audio device on 'fd'.
 */
int
audio__setplayhdr(int fd, Audio_hdr *hdrp, unsigned int which)
{
	Audio_hdr		thdr;
	Audio_info		info;
	struct audio_prinfo	*prinfo;
	int			err;

	if (which & AUDIO__PLAY)
		prinfo = &info.play;
	else if (which & AUDIO__RECORD)
		prinfo = &info.record;
	else
		return (AUDIO_ERR_BADARG);

	if (which & AUDIO__SET) {
		thdr = *hdrp;			/* save original hdr */
		AUDIO_INITINFO(&info);
		prinfo->sample_rate = hdrp->sample_rate;
		prinfo->channels = hdrp->channels;
		prinfo->encoding = hdrp->encoding;
		prinfo->precision = hdrp->bytes_per_unit * 8;
		err = audio_setinfo(fd, &info);
	} else {
		err = audio_getinfo(fd, &info);
	}

	/* Decode back into the header structure */
	/* since the I_SMSG is set, upon completion of updating 	*/
	/* the format, the driver sends the msg  so the 		*/
	/* system call is interrupted.  For now, just ignore this 	*/
	if ((err == AUDIO_SUCCESS) || (errno == EINTR)) {
		hdrp->sample_rate = prinfo->sample_rate;
		hdrp->channels = prinfo->channels;
		hdrp->data_size = AUDIO_UNKNOWN_SIZE;
		hdrp->encoding = prinfo->encoding;
		switch (hdrp->encoding) {
		case AUDIO_ENCODING_ULAW:
		case AUDIO_ENCODING_ALAW:
		case AUDIO_ENCODING_LINEAR:
		case AUDIO_ENCODING_LINEAR8:
		case AUDIO_ENCODING_FLOAT:
			hdrp->bytes_per_unit = prinfo->precision / 8;
			hdrp->samples_per_unit = 1;
			break;
		default:
			return (AUDIO_ERR_ENCODING);
		}
		if (which & AUDIO__SET) {
			/* Check to see if *all* changes took effect */
			if (audio_cmp_hdr(hdrp, &thdr) != 0)
				return (AUDIO_ERR_NOEFFECT);
		}
	}
	return (err);
}


/*
 * Attempt to configure the audio device to match a particular encoding.
 */

/*
 * Set and/or set individual state values.
 * This routine is generally invoked by using the audio_set_*()
 * and audio_get_*() macros.
 * The 'valp' argument is always a pointer to an unsigned int.
 * Conversions to/from (unsigned char) flags are taken care of.
 */
int
audio__setval(int fd, unsigned int *valp, unsigned int which)
{
	Audio_info		info;
	struct audio_prinfo	*prinfo;
	int			err;
	unsigned		*up;
	unsigned char		*cp;

	/* Set a pointer to the value of interest */
	if (which & AUDIO__PLAY)
		prinfo = &info.play;
	else if (which & AUDIO__RECORD)
		prinfo = &info.record;
	else if ((which & AUDIO__SETVAL_MASK) != AUDIO__MONGAIN)
		return (AUDIO_ERR_BADARG);

	up = NULL;
	switch (which & AUDIO__SETVAL_MASK) {
	case AUDIO__PORT:
		up = &prinfo->port; break;
	case AUDIO__SAMPLES:
		up = &prinfo->samples; break;
	case AUDIO__ERROR:
		cp = &prinfo->error; break;
	case AUDIO__EOF:
		up = &prinfo->eof; break;
	case AUDIO__OPEN:
		cp = &prinfo->open; break;
	case AUDIO__ACTIVE:
		cp = &prinfo->active; break;
	case AUDIO__WAITING:
		cp = &prinfo->waiting; break;
	case AUDIO__GAIN:
		up = &prinfo->gain; break;
	case AUDIO__MONGAIN:
		up = &info.monitor_gain; break;
	case AUDIO__BALANCE:
		cp = &prinfo->balance; break;
	default:
		return (AUDIO_ERR_BADARG);
	}

	if (which & AUDIO__SET) {
		/* Init so that only the value of interest is changed */
		AUDIO_INITINFO(&info);
		if (up != NULL) {
			*up = *valp;
		} else {
			*cp = (unsigned char) *valp;
		}
		err = audio_setinfo(fd, &info);
	} else {
		err = audio_getinfo(fd, &info);
	}
	if (err == AUDIO_SUCCESS) {
		if (up != NULL)
			*valp = *up;
		else
			*valp = (unsigned)*cp;
	}
	return (err);
}

/*
 * Get/set gain value.
 * NOTE: legal values are floating-point double 0. - 1.
 */
int
audio__setgain(int fd, double *valp, unsigned int which)
{
	int		err;
	unsigned	x;

	if (which & AUDIO__SET) {
		if ((*valp < 0.) || (*valp > 1.))
			return (AUDIO_ERR_BADARG);

		/* Map value into legal range */
		x = ((unsigned)(*valp * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) +
		    AUDIO_MIN_GAIN;
	}

	/* Get or set the new value */
	err = audio__setval(fd, &x, which);
	if (err == AUDIO_SUCCESS) {
		/* Map value back to double */
		*valp = ((double)(x - AUDIO_MIN_GAIN) /
		    (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
	}
	return (err);
}

/*
 * Set Pause/resume flags.
 * Can set play and record individually or together.
 */
int
audio__setpause(int fd, unsigned int which)
{
	Audio_info	info;
	int		err;
	unsigned char	x;

	AUDIO_INITINFO(&info);

	if ((which & AUDIO__SETVAL_MASK) == AUDIO__PAUSE) {
		x = TRUE;
	} else if ((which & AUDIO__SETVAL_MASK) == AUDIO__RESUME) {
		x = FALSE;
	} else {
		return (AUDIO_ERR_BADARG);
	}

	if (which & AUDIO__PLAY) {
		info.play.pause = x;
	}
	if (which & AUDIO__RECORD) {
		info.record.pause = x;
	}

	/* Set the new value */
	err = audio_setinfo(fd, &info);

	/* Check to see if this took effect */
	if (err == AUDIO_SUCCESS) {
		if (((which & AUDIO__PLAY) && (info.play.pause != x)) ||
		    ((which & AUDIO__RECORD) && (info.record.pause != x)))
			return (AUDIO_ERR_NOEFFECT);
	}
	return (err);
}


/*
 * Flush play and/or record buffers.
 */
int
audio__flush(int fd, unsigned int which)
{
	int		flag;

	flag = (which & AUDIO__PLAY) ? FLUSHW : 0;
	flag |= (which & AUDIO__RECORD) ? FLUSHR : 0;

	return ((ioctl(fd, I_FLUSH, flag) < 0) ?
	    AUDIO_UNIXERROR : AUDIO_SUCCESS);
}


/*
 * Wait synchronously for output buffers to finish playing.
 * If 'sig' is TRUE, signals may interrupt the drain.
 */
int
audio_drain(int fd, int sig)
{
	while (ioctl(fd, AUDIO_DRAIN, 0) < 0) {
		if (errno != EINTR) {
			return (AUDIO_UNIXERROR);
		}

		if (sig) {
			return (AUDIO_ERR_INTERRUPTED);
		}
	}
	return (AUDIO_SUCCESS);
}

/*
 * Write an EOF marker to the output audio stream.
 */
int
audio_play_eof(int fd)
{
	return (write(fd, (char *)NULL, 0) < 0 ?
	    AUDIO_UNIXERROR : AUDIO_SUCCESS);
}