xref: /illumos-gate/usr/src/cmd/audio/utilities/device_ctl.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1989-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains routines to read and write the audio device state.
31  */
32 
33 #include <errno.h>
34 #include <stropts.h>
35 #include <sys/types.h>
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <sys/ioctl.h>
39 
40 #include <libaudio_impl.h>
41 #include <audio_errno.h>
42 #include <audio_hdr.h>
43 #include <audio_device.h>
44 
45 
46 /*
47  * Get device information structure.
48  */
49 int
50 audio_getinfo(int fd, Audio_info *ip)
51 {
52 	if (ioctl(fd, AUDIO_GETINFO, (char *)ip) < 0) {
53 		return (AUDIO_UNIXERROR);
54 	} else {
55 		return (AUDIO_SUCCESS);
56 	}
57 }
58 
59 /*
60  * Set device information structure.
61  * The calling routine should use AUDIO_INITINFO prior to setting new values.
62  */
63 int
64 audio_setinfo(int fd, Audio_info *ip)
65 {
66 	if (ioctl(fd, AUDIO_SETINFO, (char *)ip) < 0) {
67 		return (AUDIO_UNIXERROR);
68 	} else {
69 		return (AUDIO_SUCCESS);
70 	}
71 }
72 
73 /*
74  * Return an Audio_hdr corresponding to the encoding configuration
75  * of the audio device on 'fd'.
76  */
77 int
78 audio__setplayhdr(int fd, Audio_hdr *hdrp, unsigned int which)
79 {
80 	Audio_hdr		thdr;
81 	Audio_info		info;
82 	struct audio_prinfo	*prinfo;
83 	int			err;
84 
85 	if (which & AUDIO__PLAY)
86 		prinfo = &info.play;
87 	else if (which & AUDIO__RECORD)
88 		prinfo = &info.record;
89 	else
90 		return (AUDIO_ERR_BADARG);
91 
92 	if (which & AUDIO__SET) {
93 		thdr = *hdrp;			/* save original hdr */
94 		AUDIO_INITINFO(&info);
95 		prinfo->sample_rate = hdrp->sample_rate;
96 		prinfo->channels = hdrp->channels;
97 		prinfo->encoding = hdrp->encoding;
98 		prinfo->precision = hdrp->bytes_per_unit * 8;
99 		err = audio_setinfo(fd, &info);
100 	} else {
101 		err = audio_getinfo(fd, &info);
102 	}
103 
104 	/* Decode back into the header structure */
105 	/* since the I_SMSG is set, upon completion of updating 	*/
106 	/* the format, the driver sends the msg  so the 		*/
107 	/* system call is interrupted.  For now, just ignore this 	*/
108 	if ((err == AUDIO_SUCCESS) || (errno == EINTR)) {
109 		hdrp->sample_rate = prinfo->sample_rate;
110 		hdrp->channels = prinfo->channels;
111 		hdrp->data_size = AUDIO_UNKNOWN_SIZE;
112 		hdrp->encoding = prinfo->encoding;
113 		switch (hdrp->encoding) {
114 		case AUDIO_ENCODING_ULAW:
115 		case AUDIO_ENCODING_ALAW:
116 		case AUDIO_ENCODING_LINEAR:
117 		case AUDIO_ENCODING_LINEAR8:
118 		case AUDIO_ENCODING_FLOAT:
119 			hdrp->bytes_per_unit = prinfo->precision / 8;
120 			hdrp->samples_per_unit = 1;
121 			break;
122 		default:
123 			return (AUDIO_ERR_ENCODING);
124 		}
125 		if (which & AUDIO__SET) {
126 			/* Check to see if *all* changes took effect */
127 			if (audio_cmp_hdr(hdrp, &thdr) != 0)
128 				return (AUDIO_ERR_NOEFFECT);
129 		}
130 	}
131 	return (err);
132 }
133 
134 
135 /*
136  * Attempt to configure the audio device to match a particular encoding.
137  */
138 
139 /*
140  * Set and/or set individual state values.
141  * This routine is generally invoked by using the audio_set_*()
142  * and audio_get_*() macros.
143  * The 'valp' argument is always a pointer to an unsigned int.
144  * Conversions to/from (unsigned char) flags are taken care of.
145  */
146 int
147 audio__setval(int fd, unsigned int *valp, unsigned int which)
148 {
149 	Audio_info		info;
150 	struct audio_prinfo	*prinfo;
151 	int			err;
152 	unsigned		*up;
153 	unsigned char		*cp;
154 
155 	/* Set a pointer to the value of interest */
156 	if (which & AUDIO__PLAY)
157 		prinfo = &info.play;
158 	else if (which & AUDIO__RECORD)
159 		prinfo = &info.record;
160 	else if ((which & AUDIO__SETVAL_MASK) != AUDIO__MONGAIN)
161 		return (AUDIO_ERR_BADARG);
162 
163 	up = NULL;
164 	switch (which & AUDIO__SETVAL_MASK) {
165 	case AUDIO__PORT:
166 		up = &prinfo->port; break;
167 	case AUDIO__SAMPLES:
168 		up = &prinfo->samples; break;
169 	case AUDIO__ERROR:
170 		cp = &prinfo->error; break;
171 	case AUDIO__EOF:
172 		up = &prinfo->eof; break;
173 	case AUDIO__OPEN:
174 		cp = &prinfo->open; break;
175 	case AUDIO__ACTIVE:
176 		cp = &prinfo->active; break;
177 	case AUDIO__WAITING:
178 		cp = &prinfo->waiting; break;
179 	case AUDIO__GAIN:
180 		up = &prinfo->gain; break;
181 	case AUDIO__MONGAIN:
182 		up = &info.monitor_gain; break;
183 	case AUDIO__BALANCE:
184 		cp = &prinfo->balance; break;
185 	default:
186 		return (AUDIO_ERR_BADARG);
187 	}
188 
189 	if (which & AUDIO__SET) {
190 		/* Init so that only the value of interest is changed */
191 		AUDIO_INITINFO(&info);
192 		if (up != NULL) {
193 			*up = *valp;
194 		} else {
195 			*cp = (unsigned char) *valp;
196 		}
197 		err = audio_setinfo(fd, &info);
198 	} else {
199 		err = audio_getinfo(fd, &info);
200 	}
201 	if (err == AUDIO_SUCCESS) {
202 		if (up != NULL)
203 			*valp = *up;
204 		else
205 			*valp = (unsigned)*cp;
206 	}
207 	return (err);
208 }
209 
210 /*
211  * Get/set gain value.
212  * NOTE: legal values are floating-point double 0. - 1.
213  */
214 int
215 audio__setgain(int fd, double *valp, unsigned int which)
216 {
217 	int		err;
218 	unsigned	x;
219 
220 	if (which & AUDIO__SET) {
221 		if ((*valp < 0.) || (*valp > 1.))
222 			return (AUDIO_ERR_BADARG);
223 
224 		/* Map value into legal range */
225 		x = ((unsigned)(*valp * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) +
226 		    AUDIO_MIN_GAIN;
227 	}
228 
229 	/* Get or set the new value */
230 	err = audio__setval(fd, &x, which);
231 	if (err == AUDIO_SUCCESS) {
232 		/* Map value back to double */
233 		*valp = ((double)(x - AUDIO_MIN_GAIN) /
234 		    (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
235 	}
236 	return (err);
237 }
238 
239 /*
240  * Set Pause/resume flags.
241  * Can set play and record individually or together.
242  */
243 int
244 audio__setpause(int fd, unsigned int which)
245 {
246 	Audio_info	info;
247 	int		err;
248 	unsigned char	x;
249 
250 	AUDIO_INITINFO(&info);
251 
252 	if ((which & AUDIO__SETVAL_MASK) == AUDIO__PAUSE) {
253 		x = TRUE;
254 	} else if ((which & AUDIO__SETVAL_MASK) == AUDIO__RESUME) {
255 		x = FALSE;
256 	} else {
257 		return (AUDIO_ERR_BADARG);
258 	}
259 
260 	if (which & AUDIO__PLAY) {
261 		info.play.pause = x;
262 	}
263 	if (which & AUDIO__RECORD) {
264 		info.record.pause = x;
265 	}
266 
267 	/* Set the new value */
268 	err = audio_setinfo(fd, &info);
269 
270 	/* Check to see if this took effect */
271 	if (err == AUDIO_SUCCESS) {
272 		if (((which & AUDIO__PLAY) && (info.play.pause != x)) ||
273 		    ((which & AUDIO__RECORD) && (info.record.pause != x)))
274 			return (AUDIO_ERR_NOEFFECT);
275 	}
276 	return (err);
277 }
278 
279 
280 /*
281  * Flush play and/or record buffers.
282  */
283 int
284 audio__flush(int fd, unsigned int which)
285 {
286 	int		flag;
287 
288 	flag = (which & AUDIO__PLAY) ? FLUSHW : 0;
289 	flag |= (which & AUDIO__RECORD) ? FLUSHR : 0;
290 
291 	return ((ioctl(fd, I_FLUSH, flag) < 0) ?
292 	    AUDIO_UNIXERROR : AUDIO_SUCCESS);
293 }
294 
295 
296 /*
297  * Wait synchronously for output buffers to finish playing.
298  * If 'sig' is TRUE, signals may interrupt the drain.
299  */
300 int
301 audio_drain(int fd, int sig)
302 {
303 	while (ioctl(fd, AUDIO_DRAIN, 0) < 0) {
304 		if (errno != EINTR) {
305 			return (AUDIO_UNIXERROR);
306 		}
307 
308 		if (sig) {
309 			return (AUDIO_ERR_INTERRUPTED);
310 		}
311 	}
312 	return (AUDIO_SUCCESS);
313 }
314 
315 /*
316  * Write an EOF marker to the output audio stream.
317  */
318 int
319 audio_play_eof(int fd)
320 {
321 	return (write(fd, (char *)NULL, 0) < 0 ?
322 	    AUDIO_UNIXERROR : AUDIO_SUCCESS);
323 }
324