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