xref: /openbsd-src/lib/libsndio/sio_sun.c (revision 82bfc72b1218795115ee18f228926ca6fca1ed49)
1*82bfc72bSratchov /*	$OpenBSD: sio_sun.c,v 1.2 2011/04/16 10:52:22 ratchov Exp $	*/
2b041ccb2Sratchov /*
3b041ccb2Sratchov  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4b041ccb2Sratchov  *
5b041ccb2Sratchov  * Permission to use, copy, modify, and distribute this software for any
6b041ccb2Sratchov  * purpose with or without fee is hereby granted, provided that the above
7b041ccb2Sratchov  * copyright notice and this permission notice appear in all copies.
8b041ccb2Sratchov  *
9b041ccb2Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b041ccb2Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b041ccb2Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b041ccb2Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b041ccb2Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b041ccb2Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b041ccb2Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b041ccb2Sratchov  */
17b041ccb2Sratchov /*
18b041ccb2Sratchov  * TODO:
19b041ccb2Sratchov  *
20b041ccb2Sratchov  * remove filling code from sio_sun_write() and create sio_sun_fill()
21b041ccb2Sratchov  *
22b041ccb2Sratchov  * allow block size to be set
23b041ccb2Sratchov  *
24b041ccb2Sratchov  * call hdl->cb_pos() from sio_sun_read() and sio_sun_write(), or better:
25b041ccb2Sratchov  * implement generic blocking sio_read() and sio_write() with poll(2)
26b041ccb2Sratchov  * and use non-blocking sio_ops only
27b041ccb2Sratchov  */
28b041ccb2Sratchov 
29b041ccb2Sratchov #include <sys/types.h>
30b041ccb2Sratchov #include <sys/ioctl.h>
31b041ccb2Sratchov #include <sys/audioio.h>
32b041ccb2Sratchov #include <sys/stat.h>
33b041ccb2Sratchov 
34b041ccb2Sratchov #include <errno.h>
35b041ccb2Sratchov #include <fcntl.h>
36b041ccb2Sratchov #include <limits.h>
37b041ccb2Sratchov #include <poll.h>
38b041ccb2Sratchov #include <stdio.h>
39b041ccb2Sratchov #include <stdlib.h>
40b041ccb2Sratchov #include <string.h>
41b041ccb2Sratchov #include <unistd.h>
42b041ccb2Sratchov 
43*82bfc72bSratchov #include "debug.h"
44b041ccb2Sratchov #include "sio_priv.h"
45b041ccb2Sratchov 
46b041ccb2Sratchov struct sio_sun_hdl {
47b041ccb2Sratchov 	struct sio_hdl sio;
48b041ccb2Sratchov 	int fd;
49b041ccb2Sratchov 	int filling;
50b041ccb2Sratchov 	unsigned ibpf, obpf;		/* bytes per frame */
51b041ccb2Sratchov 	unsigned ibytes, obytes;	/* bytes the hw transfered */
52b041ccb2Sratchov 	unsigned ierr, oerr;		/* frames the hw dropped */
53b041ccb2Sratchov 	int offset;			/* frames play is ahead of record */
54b041ccb2Sratchov 	int idelta, odelta;		/* position reported to client */
55b041ccb2Sratchov 	int mix_fd, mix_index;		/* /dev/mixerN stuff */
56b041ccb2Sratchov };
57b041ccb2Sratchov 
58b041ccb2Sratchov static void sio_sun_close(struct sio_hdl *);
59b041ccb2Sratchov static int sio_sun_start(struct sio_hdl *);
60b041ccb2Sratchov static int sio_sun_stop(struct sio_hdl *);
61b041ccb2Sratchov static int sio_sun_setpar(struct sio_hdl *, struct sio_par *);
62b041ccb2Sratchov static int sio_sun_getpar(struct sio_hdl *, struct sio_par *);
63b041ccb2Sratchov static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *);
64b041ccb2Sratchov static size_t sio_sun_read(struct sio_hdl *, void *, size_t);
65b041ccb2Sratchov static size_t sio_sun_write(struct sio_hdl *, const void *, size_t);
66b041ccb2Sratchov static int sio_sun_nfds(struct sio_hdl *);
67b041ccb2Sratchov static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int);
68b041ccb2Sratchov static int sio_sun_revents(struct sio_hdl *, struct pollfd *);
69b041ccb2Sratchov 
70b041ccb2Sratchov static struct sio_ops sio_sun_ops = {
71b041ccb2Sratchov 	sio_sun_close,
72b041ccb2Sratchov 	sio_sun_setpar,
73b041ccb2Sratchov 	sio_sun_getpar,
74b041ccb2Sratchov 	sio_sun_getcap,
75b041ccb2Sratchov 	sio_sun_write,
76b041ccb2Sratchov 	sio_sun_read,
77b041ccb2Sratchov 	sio_sun_start,
78b041ccb2Sratchov 	sio_sun_stop,
79b041ccb2Sratchov 	sio_sun_nfds,
80b041ccb2Sratchov 	sio_sun_pollfd,
81b041ccb2Sratchov 	sio_sun_revents,
82b041ccb2Sratchov 	NULL, /* setvol */
83b041ccb2Sratchov 	NULL, /* getvol */
84b041ccb2Sratchov };
85b041ccb2Sratchov 
86b041ccb2Sratchov /*
87b041ccb2Sratchov  * convert sun encoding to sio_par encoding
88b041ccb2Sratchov  */
89b041ccb2Sratchov static int
90b041ccb2Sratchov sio_sun_infotoenc(struct sio_sun_hdl *hdl, struct audio_prinfo *ai, struct sio_par *par)
91b041ccb2Sratchov {
92b041ccb2Sratchov 	par->msb = ai->msb;
93b041ccb2Sratchov 	par->bits = ai->precision;
94b041ccb2Sratchov 	par->bps = ai->bps;
95b041ccb2Sratchov 	switch (ai->encoding) {
96b041ccb2Sratchov 	case AUDIO_ENCODING_SLINEAR_LE:
97b041ccb2Sratchov 		par->le = 1;
98b041ccb2Sratchov 		par->sig = 1;
99b041ccb2Sratchov 		break;
100b041ccb2Sratchov 	case AUDIO_ENCODING_SLINEAR_BE:
101b041ccb2Sratchov 		par->le = 0;
102b041ccb2Sratchov 		par->sig = 1;
103b041ccb2Sratchov 		break;
104b041ccb2Sratchov 	case AUDIO_ENCODING_ULINEAR_LE:
105b041ccb2Sratchov 		par->le = 1;
106b041ccb2Sratchov 		par->sig = 0;
107b041ccb2Sratchov 		break;
108b041ccb2Sratchov 	case AUDIO_ENCODING_ULINEAR_BE:
109b041ccb2Sratchov 		par->le = 0;
110b041ccb2Sratchov 		par->sig = 0;
111b041ccb2Sratchov 		break;
112b041ccb2Sratchov 	case AUDIO_ENCODING_SLINEAR:
113b041ccb2Sratchov 		par->le = SIO_LE_NATIVE;
114b041ccb2Sratchov 		par->sig = 1;
115b041ccb2Sratchov 		break;
116b041ccb2Sratchov 	case AUDIO_ENCODING_ULINEAR:
117b041ccb2Sratchov 		par->le = SIO_LE_NATIVE;
118b041ccb2Sratchov 		par->sig = 0;
119b041ccb2Sratchov 		break;
120b041ccb2Sratchov 	default:
121b041ccb2Sratchov 		DPRINTF("sio_sun_infotoenc: unsupported encoding\n");
122b041ccb2Sratchov 		hdl->sio.eof = 1;
123b041ccb2Sratchov 		return 0;
124b041ccb2Sratchov 	}
125b041ccb2Sratchov 	return 1;
126b041ccb2Sratchov }
127b041ccb2Sratchov 
128b041ccb2Sratchov /*
129b041ccb2Sratchov  * convert sio_par encoding to sun encoding
130b041ccb2Sratchov  */
131b041ccb2Sratchov static void
132b041ccb2Sratchov sio_sun_enctoinfo(struct sio_sun_hdl *hdl, unsigned *renc, struct sio_par *par)
133b041ccb2Sratchov {
134b041ccb2Sratchov 	if (par->le == ~0U && par->sig == ~0U) {
135b041ccb2Sratchov 		*renc = ~0U;
136b041ccb2Sratchov 	} else if (par->le == ~0U || par->sig == ~0U) {
137b041ccb2Sratchov 		*renc = AUDIO_ENCODING_SLINEAR;
138b041ccb2Sratchov 	} else if (par->le && par->sig) {
139b041ccb2Sratchov 		*renc = AUDIO_ENCODING_SLINEAR_LE;
140b041ccb2Sratchov 	} else if (!par->le && par->sig) {
141b041ccb2Sratchov 		*renc = AUDIO_ENCODING_SLINEAR_BE;
142b041ccb2Sratchov 	} else if (par->le && !par->sig) {
143b041ccb2Sratchov 		*renc = AUDIO_ENCODING_ULINEAR_LE;
144b041ccb2Sratchov 	} else {
145b041ccb2Sratchov 		*renc = AUDIO_ENCODING_ULINEAR_BE;
146b041ccb2Sratchov 	}
147b041ccb2Sratchov }
148b041ccb2Sratchov 
149b041ccb2Sratchov /*
150b041ccb2Sratchov  * try to set the device to the given parameters and check that the
151b041ccb2Sratchov  * device can use them; return 1 on success, 0 on failure or error
152b041ccb2Sratchov  */
153b041ccb2Sratchov static int
154b041ccb2Sratchov sio_sun_tryinfo(struct sio_sun_hdl *hdl, struct sio_enc *enc,
155b041ccb2Sratchov     unsigned pchan, unsigned rchan, unsigned rate)
156b041ccb2Sratchov {
157b041ccb2Sratchov 	struct audio_info aui;
158b041ccb2Sratchov 	struct audio_prinfo *pr;
159b041ccb2Sratchov 
160b041ccb2Sratchov 	pr = (hdl->sio.mode & SIO_PLAY) ? &aui.play : &aui.record;
161b041ccb2Sratchov 
162b041ccb2Sratchov 	AUDIO_INITINFO(&aui);
163b041ccb2Sratchov 	if (enc) {
164b041ccb2Sratchov 		if (enc->le && enc->sig) {
165b041ccb2Sratchov 			pr->encoding = AUDIO_ENCODING_SLINEAR_LE;
166b041ccb2Sratchov 		} else if (!enc->le && enc->sig) {
167b041ccb2Sratchov 			pr->encoding = AUDIO_ENCODING_SLINEAR_BE;
168b041ccb2Sratchov 		} else if (enc->le && !enc->sig) {
169b041ccb2Sratchov 			pr->encoding = AUDIO_ENCODING_ULINEAR_LE;
170b041ccb2Sratchov 		} else {
171b041ccb2Sratchov 			pr->encoding = AUDIO_ENCODING_ULINEAR_BE;
172b041ccb2Sratchov 		}
173b041ccb2Sratchov 		pr->precision = enc->bits;
174b041ccb2Sratchov 	}
175b041ccb2Sratchov 	if (rate)
176b041ccb2Sratchov 		pr->sample_rate = rate;
177b041ccb2Sratchov 	if ((hdl->sio.mode & (SIO_PLAY | SIO_REC)) == (SIO_PLAY | SIO_REC))
178b041ccb2Sratchov 		aui.record = aui.play;
179b041ccb2Sratchov 	if (pchan && (hdl->sio.mode & SIO_PLAY))
180b041ccb2Sratchov 		aui.play.channels = pchan;
181b041ccb2Sratchov 	if (rchan && (hdl->sio.mode & SIO_REC))
182b041ccb2Sratchov 		aui.record.channels = rchan;
183b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
184b041ccb2Sratchov 		if (errno == EINVAL)
185b041ccb2Sratchov 			return 0;
186b041ccb2Sratchov 		DPERROR("sio_sun_tryinfo: setinfo");
187b041ccb2Sratchov 		hdl->sio.eof = 1;
188b041ccb2Sratchov 		return 0;
189b041ccb2Sratchov 	}
190b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
191b041ccb2Sratchov 		DPERROR("sio_sun_tryinfo: getinfo");
192b041ccb2Sratchov 		hdl->sio.eof = 1;
193b041ccb2Sratchov 		return 0;
194b041ccb2Sratchov 	}
195b041ccb2Sratchov 	if (pchan && aui.play.channels != pchan)
196b041ccb2Sratchov 		return 0;
197b041ccb2Sratchov 	if (rchan && aui.record.channels != rchan)
198b041ccb2Sratchov 		return 0;
199b041ccb2Sratchov 	if (rate) {
200b041ccb2Sratchov 		if ((hdl->sio.mode & SIO_PLAY) &&
201b041ccb2Sratchov 		    (aui.play.sample_rate != rate))
202b041ccb2Sratchov 			return 0;
203b041ccb2Sratchov 		if ((hdl->sio.mode & SIO_REC) &&
204b041ccb2Sratchov 		    (aui.record.sample_rate != rate))
205b041ccb2Sratchov 			return 0;
206b041ccb2Sratchov 	}
207b041ccb2Sratchov 	return 1;
208b041ccb2Sratchov }
209b041ccb2Sratchov 
210b041ccb2Sratchov /*
211b041ccb2Sratchov  * guess device capabilities
212b041ccb2Sratchov  */
213b041ccb2Sratchov static int
214b041ccb2Sratchov sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap)
215b041ccb2Sratchov {
216b041ccb2Sratchov #define NCHANS (sizeof(chans) / sizeof(chans[0]))
217b041ccb2Sratchov #define NRATES (sizeof(rates) / sizeof(rates[0]))
218b041ccb2Sratchov 	static unsigned chans[] = {
219b041ccb2Sratchov 		1, 2, 4, 6, 8, 10, 12
220b041ccb2Sratchov 	};
221b041ccb2Sratchov 	static unsigned rates[] = {
222b041ccb2Sratchov 		8000, 11025, 12000, 16000, 22050, 24000,
223b041ccb2Sratchov 		32000, 44100, 48000, 64000, 88200, 96000
224b041ccb2Sratchov 	};
225b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
226b041ccb2Sratchov 	struct sio_par savepar;
227b041ccb2Sratchov 	struct audio_encoding ae;
228b041ccb2Sratchov 	unsigned nenc = 0, nconf = 0;
229b041ccb2Sratchov 	unsigned enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
230b041ccb2Sratchov 	unsigned i, j, conf;
231b041ccb2Sratchov 
232b041ccb2Sratchov 	if (!sio_sun_getpar(&hdl->sio, &savepar))
233b041ccb2Sratchov 		return 0;
234b041ccb2Sratchov 
235b041ccb2Sratchov 	/*
236b041ccb2Sratchov 	 * fill encoding list
237b041ccb2Sratchov 	 */
238b041ccb2Sratchov 	for (ae.index = 0; nenc < SIO_NENC; ae.index++) {
239b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_GETENC, &ae) < 0) {
240b041ccb2Sratchov 			if (errno == EINVAL)
241b041ccb2Sratchov 				break;
242b041ccb2Sratchov 			DPERROR("sio_sun_getcap: getenc");
243b041ccb2Sratchov 			hdl->sio.eof = 1;
244b041ccb2Sratchov 			return 0;
245b041ccb2Sratchov 		}
246b041ccb2Sratchov 		if (ae.flags & AUDIO_ENCODINGFLAG_EMULATED)
247b041ccb2Sratchov 			continue;
248b041ccb2Sratchov 		if (ae.encoding == AUDIO_ENCODING_SLINEAR_LE) {
249b041ccb2Sratchov 			cap->enc[nenc].le = 1;
250b041ccb2Sratchov 			cap->enc[nenc].sig = 1;
251b041ccb2Sratchov 		} else if (ae.encoding == AUDIO_ENCODING_SLINEAR_BE) {
252b041ccb2Sratchov 			cap->enc[nenc].le = 0;
253b041ccb2Sratchov 			cap->enc[nenc].sig = 1;
254b041ccb2Sratchov 		} else if (ae.encoding == AUDIO_ENCODING_ULINEAR_LE) {
255b041ccb2Sratchov 			cap->enc[nenc].le = 1;
256b041ccb2Sratchov 			cap->enc[nenc].sig = 0;
257b041ccb2Sratchov 		} else if (ae.encoding == AUDIO_ENCODING_ULINEAR_BE) {
258b041ccb2Sratchov 			cap->enc[nenc].le = 0;
259b041ccb2Sratchov 			cap->enc[nenc].sig = 0;
260b041ccb2Sratchov 		} else if (ae.encoding == AUDIO_ENCODING_SLINEAR) {
261b041ccb2Sratchov 			cap->enc[nenc].le = SIO_LE_NATIVE;
262b041ccb2Sratchov 			cap->enc[nenc].sig = 1;
263b041ccb2Sratchov 		} else if (ae.encoding == AUDIO_ENCODING_ULINEAR) {
264b041ccb2Sratchov 			cap->enc[nenc].le = SIO_LE_NATIVE;
265b041ccb2Sratchov 			cap->enc[nenc].sig = 0;
266b041ccb2Sratchov 		} else {
267b041ccb2Sratchov 			/* unsipported encoding */
268b041ccb2Sratchov 			continue;
269b041ccb2Sratchov 		}
270b041ccb2Sratchov 		cap->enc[nenc].bits = ae.precision;
271b041ccb2Sratchov 		cap->enc[nenc].bps = ae.bps;
272b041ccb2Sratchov 		cap->enc[nenc].msb = ae.msb;
273b041ccb2Sratchov 		enc_map |= (1 << nenc);
274b041ccb2Sratchov 		nenc++;
275b041ccb2Sratchov 	}
276b041ccb2Sratchov 
277b041ccb2Sratchov 	/*
278b041ccb2Sratchov 	 * fill channels
279b041ccb2Sratchov 	 *
280b041ccb2Sratchov 	 * for now we're lucky: all kernel devices assume that the
281b041ccb2Sratchov 	 * number of channels and the encoding are independent so we can
282b041ccb2Sratchov 	 * use the current encoding and try various channels.
283b041ccb2Sratchov 	 */
284b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
285b041ccb2Sratchov 		memcpy(&cap->pchan, chans, NCHANS * sizeof(unsigned));
286b041ccb2Sratchov 		for (i = 0; i < NCHANS; i++) {
287b041ccb2Sratchov 			if (sio_sun_tryinfo(hdl, NULL, chans[i], 0, 0))
288b041ccb2Sratchov 				pchan_map |= (1 << i);
289b041ccb2Sratchov 		}
290b041ccb2Sratchov 	}
291b041ccb2Sratchov 	if (hdl->sio.mode & SIO_REC) {
292b041ccb2Sratchov 		memcpy(&cap->rchan, chans, NCHANS * sizeof(unsigned));
293b041ccb2Sratchov 		for (i = 0; i < NCHANS; i++) {
294b041ccb2Sratchov 			if (sio_sun_tryinfo(hdl, NULL, 0, chans[i], 0))
295b041ccb2Sratchov 				rchan_map |= (1 << i);
296b041ccb2Sratchov 		}
297b041ccb2Sratchov 	}
298b041ccb2Sratchov 
299b041ccb2Sratchov 	/*
300b041ccb2Sratchov 	 * fill rates
301b041ccb2Sratchov 	 *
302b041ccb2Sratchov 	 * rates are not independent from other parameters (eg. on
303b041ccb2Sratchov 	 * uaudio devices), so certain rates may not be allowed with
304b041ccb2Sratchov 	 * certain encodings. We have to check rates for all encodings
305b041ccb2Sratchov 	 */
306b041ccb2Sratchov 	memcpy(&cap->rate, rates, NRATES * sizeof(unsigned));
307b041ccb2Sratchov 	for (j = 0; j < nenc; j++) {
308b041ccb2Sratchov 		rate_map = 0;
309b041ccb2Sratchov 		for (i = 0; i < NRATES; i++) {
310b041ccb2Sratchov 			if (sio_sun_tryinfo(hdl, &cap->enc[j], 0, 0, rates[i]))
311b041ccb2Sratchov 				rate_map |= (1 << i);
312b041ccb2Sratchov 		}
313b041ccb2Sratchov 		for (conf = 0; conf < nconf; conf++) {
314b041ccb2Sratchov 			if (cap->confs[conf].rate == rate_map) {
315b041ccb2Sratchov 				cap->confs[conf].enc |= (1 << j);
316b041ccb2Sratchov 				break;
317b041ccb2Sratchov 			}
318b041ccb2Sratchov 		}
319b041ccb2Sratchov 		if (conf == nconf) {
320b041ccb2Sratchov 			if (nconf == SIO_NCONF)
321b041ccb2Sratchov 				break;
322b041ccb2Sratchov 			cap->confs[nconf].enc = (1 << j);
323b041ccb2Sratchov 			cap->confs[nconf].pchan = pchan_map;
324b041ccb2Sratchov 			cap->confs[nconf].rchan = rchan_map;
325b041ccb2Sratchov 			cap->confs[nconf].rate = rate_map;
326b041ccb2Sratchov 			nconf++;
327b041ccb2Sratchov 		}
328b041ccb2Sratchov 	}
329b041ccb2Sratchov 	cap->nconf = nconf;
330b041ccb2Sratchov 	if (!sio_sun_setpar(&hdl->sio, &savepar))
331b041ccb2Sratchov 		return 0;
332b041ccb2Sratchov 	return 1;
333b041ccb2Sratchov #undef NCHANS
334b041ccb2Sratchov #undef NRATES
335b041ccb2Sratchov }
336b041ccb2Sratchov 
337b041ccb2Sratchov struct sio_hdl *
338b041ccb2Sratchov sio_sun_open(const char *str, unsigned mode, int nbio)
339b041ccb2Sratchov {
340b041ccb2Sratchov 	int fd, flags, fullduplex;
341b041ccb2Sratchov 	struct audio_info aui;
342b041ccb2Sratchov 	struct sio_sun_hdl *hdl;
343b041ccb2Sratchov 	struct sio_par par;
344b041ccb2Sratchov 	char path[PATH_MAX];
345b041ccb2Sratchov 
346b041ccb2Sratchov 	hdl = malloc(sizeof(struct sio_sun_hdl));
347b041ccb2Sratchov 	if (hdl == NULL)
348b041ccb2Sratchov 		return NULL;
349b041ccb2Sratchov 	sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
350b041ccb2Sratchov 
351b041ccb2Sratchov 	snprintf(path, sizeof(path), "/dev/audio%s", str);
352b041ccb2Sratchov 	if (mode == (SIO_PLAY | SIO_REC))
353b041ccb2Sratchov 		flags = O_RDWR;
354b041ccb2Sratchov 	else
355b041ccb2Sratchov 		flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
356b041ccb2Sratchov 
357b041ccb2Sratchov 	while ((fd = open(path, flags | O_NONBLOCK)) < 0) {
358b041ccb2Sratchov 		if (errno == EINTR)
359b041ccb2Sratchov 			continue;
360b041ccb2Sratchov 		DPERROR(path);
361b041ccb2Sratchov 		goto bad_free;
362b041ccb2Sratchov 	}
363b041ccb2Sratchov 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
364b041ccb2Sratchov 		DPERROR("FD_CLOEXEC");
365b041ccb2Sratchov 		goto bad_close;
366b041ccb2Sratchov 	}
367b041ccb2Sratchov 
368b041ccb2Sratchov 	/*
369b041ccb2Sratchov 	 * pause the device
370b041ccb2Sratchov 	 */
371b041ccb2Sratchov 	AUDIO_INITINFO(&aui);
372b041ccb2Sratchov 	if (mode & SIO_PLAY)
373b041ccb2Sratchov 		aui.play.pause = 1;
374b041ccb2Sratchov 	if (mode & SIO_REC)
375b041ccb2Sratchov 		aui.record.pause = 1;
376b041ccb2Sratchov 	if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) {
377b041ccb2Sratchov 		DPERROR("sio_open_sun: setinfo");
378b041ccb2Sratchov 		goto bad_close;
379b041ccb2Sratchov 	}
380b041ccb2Sratchov 	/*
381b041ccb2Sratchov 	 * If both play and record are requested then
382b041ccb2Sratchov 	 * set full duplex mode.
383b041ccb2Sratchov 	 */
384b041ccb2Sratchov 	if (mode == (SIO_PLAY | SIO_REC)) {
385b041ccb2Sratchov 		fullduplex = 1;
386b041ccb2Sratchov 		if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) {
387b041ccb2Sratchov 			DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path);
388b041ccb2Sratchov 			goto bad_close;
389b041ccb2Sratchov 		}
390b041ccb2Sratchov 	}
391b041ccb2Sratchov 	hdl->fd = fd;
392b041ccb2Sratchov 
393b041ccb2Sratchov 	/*
394b041ccb2Sratchov 	 * Default parameters may not be compatible with libsndio (eg. mulaw
395b041ccb2Sratchov 	 * encodings, different playback and recording parameters, etc...), so
396b041ccb2Sratchov 	 * set parameters to a random value. If the requested parameters are
397b041ccb2Sratchov 	 * not supported by the device, then sio_setpar() will pick supported
398b041ccb2Sratchov 	 * ones.
399b041ccb2Sratchov 	 */
400b041ccb2Sratchov 	sio_initpar(&par);
401b041ccb2Sratchov 	par.rate = 48000;
402b041ccb2Sratchov 	par.le = SIO_LE_NATIVE;
403b041ccb2Sratchov 	par.sig = 1;
404b041ccb2Sratchov 	par.bits = 16;
405b041ccb2Sratchov 	par.appbufsz = 1200;
406b041ccb2Sratchov 	if (!sio_setpar(&hdl->sio, &par))
407b041ccb2Sratchov 		goto bad_close;
408b041ccb2Sratchov 	return (struct sio_hdl *)hdl;
409b041ccb2Sratchov  bad_close:
410b041ccb2Sratchov 	while (close(fd) < 0 && errno == EINTR)
411b041ccb2Sratchov 		; /* retry */
412b041ccb2Sratchov  bad_free:
413b041ccb2Sratchov 	free(hdl);
414b041ccb2Sratchov 	return NULL;
415b041ccb2Sratchov }
416b041ccb2Sratchov 
417b041ccb2Sratchov static void
418b041ccb2Sratchov sio_sun_close(struct sio_hdl *sh)
419b041ccb2Sratchov {
420b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
421b041ccb2Sratchov 
422b041ccb2Sratchov 	while (close(hdl->fd) < 0 && errno == EINTR)
423b041ccb2Sratchov 		; /* retry */
424b041ccb2Sratchov 	free(hdl);
425b041ccb2Sratchov }
426b041ccb2Sratchov 
427b041ccb2Sratchov static int
428b041ccb2Sratchov sio_sun_start(struct sio_hdl *sh)
429b041ccb2Sratchov {
430b041ccb2Sratchov 	struct sio_par par;
431b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
432b041ccb2Sratchov 	struct audio_info aui;
433b041ccb2Sratchov 
434b041ccb2Sratchov 	if (!sio_getpar(&hdl->sio, &par))
435b041ccb2Sratchov 		return 0;
436b041ccb2Sratchov 	hdl->obpf = par.pchan * par.bps;
437b041ccb2Sratchov 	hdl->ibpf = par.rchan * par.bps;
438b041ccb2Sratchov 	hdl->ibytes = 0;
439b041ccb2Sratchov 	hdl->obytes = 0;
440b041ccb2Sratchov 	hdl->ierr = 0;
441b041ccb2Sratchov 	hdl->oerr = 0;
442b041ccb2Sratchov 	hdl->offset = 0;
443b041ccb2Sratchov 	hdl->idelta = 0;
444b041ccb2Sratchov 	hdl->odelta = 0;
445b041ccb2Sratchov 
446b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
447b041ccb2Sratchov 		/*
448b041ccb2Sratchov 		 * keep the device paused and let sio_sun_write() trigger the
449b041ccb2Sratchov 		 * start later, to avoid buffer underruns
450b041ccb2Sratchov 		 */
451b041ccb2Sratchov 		hdl->filling = 1;
452b041ccb2Sratchov 	} else {
453b041ccb2Sratchov 		/*
454b041ccb2Sratchov 		 * no play buffers to fill, start now!
455b041ccb2Sratchov 		 */
456b041ccb2Sratchov 		AUDIO_INITINFO(&aui);
457b041ccb2Sratchov 		if (hdl->sio.mode & SIO_REC)
458b041ccb2Sratchov 			aui.record.pause = 0;
459b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
460b041ccb2Sratchov 			DPERROR("sio_sun_start: setinfo");
461b041ccb2Sratchov 			hdl->sio.eof = 1;
462b041ccb2Sratchov 			return 0;
463b041ccb2Sratchov 		}
464b041ccb2Sratchov 		hdl->filling = 0;
465b041ccb2Sratchov 		sio_onmove_cb(&hdl->sio, 0);
466b041ccb2Sratchov 	}
467b041ccb2Sratchov 	return 1;
468b041ccb2Sratchov }
469b041ccb2Sratchov 
470b041ccb2Sratchov static int
471b041ccb2Sratchov sio_sun_stop(struct sio_hdl *sh)
472b041ccb2Sratchov {
473b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
474b041ccb2Sratchov 	struct audio_info aui;
475b041ccb2Sratchov 	int mode;
476b041ccb2Sratchov 
477b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
478b041ccb2Sratchov 		DPERROR("sio_sun_stop: getinfo");
479b041ccb2Sratchov 		hdl->sio.eof = 1;
480b041ccb2Sratchov 		return 0;
481b041ccb2Sratchov 	}
482b041ccb2Sratchov 	mode = aui.mode;
483b041ccb2Sratchov 
484b041ccb2Sratchov 	/*
485b041ccb2Sratchov 	 * there's no way to drain the device without blocking, so just
486b041ccb2Sratchov 	 * stop it until the kernel driver get fixed
487b041ccb2Sratchov 	 */
488b041ccb2Sratchov 	AUDIO_INITINFO(&aui);
489b041ccb2Sratchov 	aui.mode = 0;
490b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY)
491b041ccb2Sratchov 		aui.play.pause = 1;
492b041ccb2Sratchov 	if (hdl->sio.mode & SIO_REC)
493b041ccb2Sratchov 		aui.record.pause = 1;
494b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
495b041ccb2Sratchov 		DPERROR("sio_sun_stop: setinfo1");
496b041ccb2Sratchov 		hdl->sio.eof = 1;
497b041ccb2Sratchov 		return 0;
498b041ccb2Sratchov 	}
499b041ccb2Sratchov 	AUDIO_INITINFO(&aui);
500b041ccb2Sratchov 	aui.mode = mode;
501b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
502b041ccb2Sratchov 		DPERROR("sio_sun_stop: setinfo2");
503b041ccb2Sratchov 		hdl->sio.eof = 1;
504b041ccb2Sratchov 		return 0;
505b041ccb2Sratchov 	}
506b041ccb2Sratchov 	return 1;
507b041ccb2Sratchov }
508b041ccb2Sratchov 
509b041ccb2Sratchov static int
510b041ccb2Sratchov sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
511b041ccb2Sratchov {
512b041ccb2Sratchov #define NRETRIES 8
513b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
514b041ccb2Sratchov 	struct audio_info aui;
515b041ccb2Sratchov 	unsigned i, infr, ibpf, onfr, obpf;
516b041ccb2Sratchov 	unsigned bufsz, round;
517b041ccb2Sratchov 	unsigned rate, req_rate, prec, enc;
518b041ccb2Sratchov 
519b041ccb2Sratchov 	/*
520b041ccb2Sratchov 	 * try to set parameters until the device accepts
521b041ccb2Sratchov 	 * a common encoding and rate for play and record
522b041ccb2Sratchov 	 */
523b041ccb2Sratchov 	rate = par->rate;
524b041ccb2Sratchov 	prec = par->bits;
525b041ccb2Sratchov 	sio_sun_enctoinfo(hdl, &enc, par);
526b041ccb2Sratchov 	for (i = 0;; i++) {
527b041ccb2Sratchov 		if (i == NRETRIES) {
528b041ccb2Sratchov 			DPRINTF("sio_sun_setpar: couldn't set parameters\n");
529b041ccb2Sratchov 			hdl->sio.eof = 1;
530b041ccb2Sratchov 			return 0;
531b041ccb2Sratchov 		}
532b041ccb2Sratchov 		AUDIO_INITINFO(&aui);
533b041ccb2Sratchov 		if (hdl->sio.mode & SIO_PLAY) {
534b041ccb2Sratchov 			aui.play.sample_rate = rate;
535b041ccb2Sratchov 			aui.play.precision = prec;
536b041ccb2Sratchov 			aui.play.encoding = enc;
537b041ccb2Sratchov 			aui.play.channels = par->pchan;
538b041ccb2Sratchov 		}
539b041ccb2Sratchov 		if (hdl->sio.mode & SIO_REC) {
540b041ccb2Sratchov 			aui.record.sample_rate = rate;
541b041ccb2Sratchov 			aui.record.precision = prec;
542b041ccb2Sratchov 			aui.record.encoding = enc;
543b041ccb2Sratchov 			aui.record.channels = par->rchan;
544b041ccb2Sratchov 		}
545b041ccb2Sratchov 		DPRINTF("sio_sun_setpar: %i: trying pars = %u/%u/%u\n",
546b041ccb2Sratchov 		    i, rate, prec, enc);
547b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) {
548b041ccb2Sratchov 			DPERROR("sio_sun_setpar: setinfo(pars)");
549b041ccb2Sratchov 			hdl->sio.eof = 1;
550b041ccb2Sratchov 			return 0;
551b041ccb2Sratchov 		}
552b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
553b041ccb2Sratchov 			DPERROR("sio_sun_setpar: getinfo(pars)");
554b041ccb2Sratchov 			hdl->sio.eof = 1;
555b041ccb2Sratchov 			return 0;
556b041ccb2Sratchov 		}
557b041ccb2Sratchov 		enc = (hdl->sio.mode & SIO_REC) ?
558b041ccb2Sratchov 		    aui.record.encoding : aui.play.encoding;
559b041ccb2Sratchov 		switch (enc) {
560b041ccb2Sratchov 		case AUDIO_ENCODING_SLINEAR_LE:
561b041ccb2Sratchov 		case AUDIO_ENCODING_SLINEAR_BE:
562b041ccb2Sratchov 		case AUDIO_ENCODING_ULINEAR_LE:
563b041ccb2Sratchov 		case AUDIO_ENCODING_ULINEAR_BE:
564b041ccb2Sratchov 		case AUDIO_ENCODING_SLINEAR:
565b041ccb2Sratchov 		case AUDIO_ENCODING_ULINEAR:
566b041ccb2Sratchov 			break;
567b041ccb2Sratchov 		default:
568b041ccb2Sratchov 			DPRINTF("sio_sun_setpar: couldn't set linear encoding\n");
569b041ccb2Sratchov 			hdl->sio.eof = 1;
570b041ccb2Sratchov 			return 0;
571b041ccb2Sratchov 		}
572b041ccb2Sratchov 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY))
573b041ccb2Sratchov 			break;
574b041ccb2Sratchov 		if (aui.play.sample_rate == aui.record.sample_rate &&
575b041ccb2Sratchov 		    aui.play.precision == aui.record.precision &&
576b041ccb2Sratchov 		    aui.play.encoding == aui.record.encoding)
577b041ccb2Sratchov 			break;
578b041ccb2Sratchov 		if (i < NRETRIES / 2) {
579b041ccb2Sratchov 			rate = aui.play.sample_rate;
580b041ccb2Sratchov 			prec = aui.play.precision;
581b041ccb2Sratchov 			enc = aui.play.encoding;
582b041ccb2Sratchov 		} else {
583b041ccb2Sratchov 			rate = aui.record.sample_rate;
584b041ccb2Sratchov 			prec = aui.record.precision;
585b041ccb2Sratchov 			enc = aui.record.encoding;
586b041ccb2Sratchov 		}
587b041ccb2Sratchov 	}
588b041ccb2Sratchov 
589b041ccb2Sratchov 	/*
590b041ccb2Sratchov 	 * If the rate that the hardware is using is different than
591b041ccb2Sratchov 	 * the requested rate, scale buffer sizes so they will be the
592b041ccb2Sratchov 	 * same time duration as what was requested.  This just gets
593b041ccb2Sratchov 	 * the rates to use for scaling, that actual scaling is done
594b041ccb2Sratchov 	 * later.
595b041ccb2Sratchov 	 */
596b041ccb2Sratchov 	rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate :
597b041ccb2Sratchov 	    aui.play.sample_rate;
598b041ccb2Sratchov 	req_rate = rate;
599b041ccb2Sratchov 	if (par->rate && par->rate != ~0U)
600b041ccb2Sratchov 		req_rate = par->rate;
601b041ccb2Sratchov 
602b041ccb2Sratchov 	/*
603b041ccb2Sratchov 	 * if block size and buffer size are not both set then
604b041ccb2Sratchov 	 * set the blocksize to half the buffer size
605b041ccb2Sratchov 	 */
606b041ccb2Sratchov 	bufsz = par->appbufsz;
607b041ccb2Sratchov 	round = par->round;
608b041ccb2Sratchov 	if (bufsz != ~0U) {
609b041ccb2Sratchov 		bufsz = bufsz * rate / req_rate;
610b041ccb2Sratchov 		if (round == ~0U)
611b041ccb2Sratchov 			round = (bufsz + 1) / 2;
612b041ccb2Sratchov 		else
613b041ccb2Sratchov 			round = round * rate / req_rate;
614b041ccb2Sratchov 	} else if (round != ~0U) {
615b041ccb2Sratchov 		round = round * rate / req_rate;
616b041ccb2Sratchov 		bufsz = round * 2;
617b041ccb2Sratchov 	} else
618b041ccb2Sratchov 		return 1;
619b041ccb2Sratchov 
620b041ccb2Sratchov 	/*
621b041ccb2Sratchov 	 * get the play/record frame size in bytes
622b041ccb2Sratchov 	 */
623b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
624b041ccb2Sratchov 		DPERROR("sio_sun_setpar: GETINFO");
625b041ccb2Sratchov 		hdl->sio.eof = 1;
626b041ccb2Sratchov 		return 0;
627b041ccb2Sratchov 	}
628b041ccb2Sratchov 	ibpf = (hdl->sio.mode & SIO_REC) ?
629b041ccb2Sratchov 	    aui.record.channels * aui.record.bps : 1;
630b041ccb2Sratchov 	obpf = (hdl->sio.mode & SIO_PLAY) ?
631b041ccb2Sratchov 	    aui.play.channels * aui.play.bps : 1;
632b041ccb2Sratchov 
633b041ccb2Sratchov 	DPRINTF("sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf);
634b041ccb2Sratchov 
635b041ccb2Sratchov 	/*
636b041ccb2Sratchov 	 * try to set parameters until the device accepts
637b041ccb2Sratchov 	 * a common block size for play and record
638b041ccb2Sratchov 	 */
639b041ccb2Sratchov 	for (i = 0; i < NRETRIES; i++) {
640b041ccb2Sratchov 		AUDIO_INITINFO(&aui);
641b041ccb2Sratchov 		aui.hiwat = (bufsz + round - 1) / round;
642b041ccb2Sratchov 		aui.lowat = aui.hiwat;
643b041ccb2Sratchov 		if (hdl->sio.mode & SIO_REC)
644b041ccb2Sratchov 			aui.record.block_size = round * ibpf;
645b041ccb2Sratchov 		if (hdl->sio.mode & SIO_PLAY)
646b041ccb2Sratchov 			aui.play.block_size = round * obpf;
647b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
648b041ccb2Sratchov 			DPERROR("sio_sun_setpar2: SETINFO");
649b041ccb2Sratchov 			hdl->sio.eof = 1;
650b041ccb2Sratchov 			return 0;
651b041ccb2Sratchov 		}
652b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
653b041ccb2Sratchov 			DPERROR("sio_sun_setpar2: GETINFO");
654b041ccb2Sratchov 			hdl->sio.eof = 1;
655b041ccb2Sratchov 			return 0;
656b041ccb2Sratchov 		}
657b041ccb2Sratchov 		infr = aui.record.block_size / ibpf;
658b041ccb2Sratchov 		onfr = aui.play.block_size / obpf;
659b041ccb2Sratchov 		DPRINTF("sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n",
660b041ccb2Sratchov 		    i, round, infr, onfr);
661b041ccb2Sratchov 
662b041ccb2Sratchov 		/*
663b041ccb2Sratchov 		 * if half-duplex or both block sizes match, we're done
664b041ccb2Sratchov 		 */
665b041ccb2Sratchov 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) {
666b041ccb2Sratchov 			DPRINTF("sio_sun_setpar: blocksize ok\n");
667b041ccb2Sratchov 			return 1;
668b041ccb2Sratchov 		}
669b041ccb2Sratchov 
670b041ccb2Sratchov 		/*
671b041ccb2Sratchov 		 * half of the retries, retry with the smaller value,
672b041ccb2Sratchov 		 * then with the larger returned value
673b041ccb2Sratchov 		 */
674b041ccb2Sratchov 		if (i < NRETRIES / 2)
675b041ccb2Sratchov 			round = infr < onfr ? infr : onfr;
676b041ccb2Sratchov 		else
677b041ccb2Sratchov 			round = infr < onfr ? onfr : infr;
678b041ccb2Sratchov 	}
679b041ccb2Sratchov 	DPRINTF("sio_sun_setpar: couldn't find a working blocksize\n");
680b041ccb2Sratchov 	hdl->sio.eof = 1;
681b041ccb2Sratchov 	return 0;
682b041ccb2Sratchov #undef NRETRIES
683b041ccb2Sratchov }
684b041ccb2Sratchov 
685b041ccb2Sratchov static int
686b041ccb2Sratchov sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
687b041ccb2Sratchov {
688b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
689b041ccb2Sratchov 	struct audio_info aui;
690b041ccb2Sratchov 
691b041ccb2Sratchov 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
692b041ccb2Sratchov 		DPERROR("sio_sun_getpar: getinfo");
693b041ccb2Sratchov 		hdl->sio.eof = 1;
694b041ccb2Sratchov 		return 0;
695b041ccb2Sratchov 	}
696b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
697b041ccb2Sratchov 		par->rate = aui.play.sample_rate;
698b041ccb2Sratchov 		if (!sio_sun_infotoenc(hdl, &aui.play, par))
699b041ccb2Sratchov 			return 0;
700b041ccb2Sratchov 	} else if (hdl->sio.mode & SIO_REC) {
701b041ccb2Sratchov 		par->rate = aui.record.sample_rate;
702b041ccb2Sratchov 		if (!sio_sun_infotoenc(hdl, &aui.record, par))
703b041ccb2Sratchov 			return 0;
704b041ccb2Sratchov 	} else
705b041ccb2Sratchov 		return 0;
706b041ccb2Sratchov 	par->pchan = (hdl->sio.mode & SIO_PLAY) ?
707b041ccb2Sratchov 	    aui.play.channels : 0;
708b041ccb2Sratchov 	par->rchan = (hdl->sio.mode & SIO_REC) ?
709b041ccb2Sratchov 	    aui.record.channels : 0;
710b041ccb2Sratchov 	par->round = (hdl->sio.mode & SIO_REC) ?
711b041ccb2Sratchov 	    aui.record.block_size / (par->bps * par->rchan) :
712b041ccb2Sratchov 	    aui.play.block_size / (par->bps * par->pchan);
713b041ccb2Sratchov 	par->appbufsz = aui.hiwat * par->round;
714b041ccb2Sratchov 	par->bufsz = par->appbufsz;
715b041ccb2Sratchov 	return 1;
716b041ccb2Sratchov }
717b041ccb2Sratchov 
718b041ccb2Sratchov /*
719b041ccb2Sratchov  * drop recorded samples to compensate xruns
720b041ccb2Sratchov  */
721b041ccb2Sratchov static int
722b041ccb2Sratchov sio_sun_rdrop(struct sio_sun_hdl *hdl)
723b041ccb2Sratchov {
724b041ccb2Sratchov #define DROP_NMAX 0x1000
725b041ccb2Sratchov 	static char dropbuf[DROP_NMAX];
726b041ccb2Sratchov 	ssize_t n, todo;
727b041ccb2Sratchov 
728b041ccb2Sratchov 	while (hdl->offset > 0) {
729b041ccb2Sratchov 		todo = hdl->offset * hdl->ibpf;
730b041ccb2Sratchov 		if (todo > DROP_NMAX)
731b041ccb2Sratchov 			todo = DROP_NMAX - DROP_NMAX % hdl->ibpf;
732b041ccb2Sratchov 		while ((n = read(hdl->fd, dropbuf, todo)) < 0) {
733b041ccb2Sratchov 			if (errno == EINTR)
734b041ccb2Sratchov 				continue;
735b041ccb2Sratchov 			if (errno != EAGAIN) {
736b041ccb2Sratchov 				DPERROR("sio_sun_rdrop: read");
737b041ccb2Sratchov 				hdl->sio.eof = 1;
738b041ccb2Sratchov 			}
739b041ccb2Sratchov 			return 0;
740b041ccb2Sratchov 		}
741b041ccb2Sratchov 		if (n == 0) {
742b041ccb2Sratchov 			DPRINTF("sio_sun_rdrop: eof\n");
743b041ccb2Sratchov 			hdl->sio.eof = 1;
744b041ccb2Sratchov 			return 0;
745b041ccb2Sratchov 		}
746b041ccb2Sratchov 		hdl->offset -= (int)n / (int)hdl->ibpf;
747b041ccb2Sratchov 		DPRINTF("sio_sun_rdrop: dropped %ld/%ld bytes\n", n, todo);
748b041ccb2Sratchov 	}
749b041ccb2Sratchov 	return 1;
750b041ccb2Sratchov }
751b041ccb2Sratchov 
752b041ccb2Sratchov static size_t
753b041ccb2Sratchov sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
754b041ccb2Sratchov {
755b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
756b041ccb2Sratchov 	ssize_t n;
757b041ccb2Sratchov 
758b041ccb2Sratchov 	if (!sio_sun_rdrop(hdl))
759b041ccb2Sratchov 		return 0;
760b041ccb2Sratchov 	while ((n = read(hdl->fd, buf, len)) < 0) {
761b041ccb2Sratchov 		if (errno == EINTR)
762b041ccb2Sratchov 			continue;
763b041ccb2Sratchov 		if (errno != EAGAIN) {
764b041ccb2Sratchov 			DPERROR("sio_sun_read: read");
765b041ccb2Sratchov 			hdl->sio.eof = 1;
766b041ccb2Sratchov 		}
767b041ccb2Sratchov 		return 0;
768b041ccb2Sratchov 	}
769b041ccb2Sratchov 	if (n == 0) {
770b041ccb2Sratchov 		DPRINTF("sio_sun_read: eof\n");
771b041ccb2Sratchov 		hdl->sio.eof = 1;
772b041ccb2Sratchov 		return 0;
773b041ccb2Sratchov 	}
774b041ccb2Sratchov 	return n;
775b041ccb2Sratchov }
776b041ccb2Sratchov 
777b041ccb2Sratchov static size_t
778b041ccb2Sratchov sio_sun_autostart(struct sio_sun_hdl *hdl)
779b041ccb2Sratchov {
780b041ccb2Sratchov 	struct audio_info aui;
781b041ccb2Sratchov 	struct pollfd pfd;
782b041ccb2Sratchov 
783b041ccb2Sratchov 	pfd.fd = hdl->fd;
784b041ccb2Sratchov 	pfd.events = POLLOUT;
785b041ccb2Sratchov 	while (poll(&pfd, 1, 0) < 0) {
786b041ccb2Sratchov 		if (errno == EINTR)
787b041ccb2Sratchov 			continue;
788b041ccb2Sratchov 		DPERROR("sio_sun_autostart: poll");
789b041ccb2Sratchov 		hdl->sio.eof = 1;
790b041ccb2Sratchov 		return 0;
791b041ccb2Sratchov 	}
792b041ccb2Sratchov 	if (!(pfd.revents & POLLOUT)) {
793b041ccb2Sratchov 		hdl->filling = 0;
794b041ccb2Sratchov 		AUDIO_INITINFO(&aui);
795b041ccb2Sratchov 		if (hdl->sio.mode & SIO_PLAY)
796b041ccb2Sratchov 			aui.play.pause = 0;
797b041ccb2Sratchov 		if (hdl->sio.mode & SIO_REC)
798b041ccb2Sratchov 			aui.record.pause = 0;
799b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
800b041ccb2Sratchov 			DPERROR("sio_sun_autostart: setinfo");
801b041ccb2Sratchov 			hdl->sio.eof = 1;
802b041ccb2Sratchov 			return 0;
803b041ccb2Sratchov 		}
804b041ccb2Sratchov 		sio_onmove_cb(&hdl->sio, 0);
805b041ccb2Sratchov 	}
806b041ccb2Sratchov 	return 1;
807b041ccb2Sratchov }
808b041ccb2Sratchov 
809b041ccb2Sratchov /*
810b041ccb2Sratchov  * insert silence to play to compensate xruns
811b041ccb2Sratchov  */
812b041ccb2Sratchov static int
813b041ccb2Sratchov sio_sun_wsil(struct sio_sun_hdl *hdl)
814b041ccb2Sratchov {
815b041ccb2Sratchov #define ZERO_NMAX 0x1000
816b041ccb2Sratchov 	static char zero[ZERO_NMAX];
817b041ccb2Sratchov 	ssize_t n, todo;
818b041ccb2Sratchov 
819b041ccb2Sratchov 	while (hdl->offset < 0) {
820b041ccb2Sratchov 		todo = (int)-hdl->offset * (int)hdl->obpf;
821b041ccb2Sratchov 		if (todo > ZERO_NMAX)
822b041ccb2Sratchov 			todo = ZERO_NMAX - ZERO_NMAX % hdl->obpf;
823b041ccb2Sratchov 		while ((n = write(hdl->fd, zero, todo)) < 0) {
824b041ccb2Sratchov 			if (errno == EINTR)
825b041ccb2Sratchov 				continue;
826b041ccb2Sratchov 			if (errno != EAGAIN) {
827b041ccb2Sratchov 				DPERROR("sio_sun_wsil: write");
828b041ccb2Sratchov 				hdl->sio.eof = 1;
829b041ccb2Sratchov 				return 0;
830b041ccb2Sratchov 			}
831b041ccb2Sratchov 			return 0;
832b041ccb2Sratchov 		}
833b041ccb2Sratchov 		hdl->offset += (int)n / (int)hdl->obpf;
834b041ccb2Sratchov 		DPRINTF("sio_sun_wsil: inserted %ld/%ld bytes\n", n, todo);
835b041ccb2Sratchov 	}
836b041ccb2Sratchov 	return 1;
837b041ccb2Sratchov }
838b041ccb2Sratchov 
839b041ccb2Sratchov 
840b041ccb2Sratchov static size_t
841b041ccb2Sratchov sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
842b041ccb2Sratchov {
843b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
844b041ccb2Sratchov 	const unsigned char *data = buf;
845b041ccb2Sratchov 	ssize_t n, todo;
846b041ccb2Sratchov 
847b041ccb2Sratchov 	if (!sio_sun_wsil(hdl))
848b041ccb2Sratchov 		return 0;
849b041ccb2Sratchov 	todo = len;
850b041ccb2Sratchov 	while ((n = write(hdl->fd, data, todo)) < 0) {
851b041ccb2Sratchov 		if (errno == EINTR)
852b041ccb2Sratchov 			continue;
853b041ccb2Sratchov 		if (errno != EAGAIN) {
854b041ccb2Sratchov 			DPERROR("sio_sun_write: write");
855b041ccb2Sratchov 			hdl->sio.eof = 1;
856b041ccb2Sratchov 		}
857b041ccb2Sratchov  		return 0;
858b041ccb2Sratchov 	}
859b041ccb2Sratchov 	if (hdl->filling) {
860b041ccb2Sratchov 		if (!sio_sun_autostart(hdl))
861b041ccb2Sratchov 			return 0;
862b041ccb2Sratchov 	}
863b041ccb2Sratchov 	return n;
864b041ccb2Sratchov }
865b041ccb2Sratchov 
866b041ccb2Sratchov static int
867b041ccb2Sratchov sio_sun_nfds(struct sio_hdl *hdl)
868b041ccb2Sratchov {
869b041ccb2Sratchov 	return 1;
870b041ccb2Sratchov }
871b041ccb2Sratchov 
872b041ccb2Sratchov static int
873b041ccb2Sratchov sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
874b041ccb2Sratchov {
875b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
876b041ccb2Sratchov 
877b041ccb2Sratchov 	pfd->fd = hdl->fd;
878b041ccb2Sratchov 	pfd->events = events;
879b041ccb2Sratchov 	return 1;
880b041ccb2Sratchov }
881b041ccb2Sratchov 
882b041ccb2Sratchov int
883b041ccb2Sratchov sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
884b041ccb2Sratchov {
885b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
886b041ccb2Sratchov 	struct audio_offset ao;
887b041ccb2Sratchov 	int xrun, dmove, dierr = 0, doerr = 0, delta;
888b041ccb2Sratchov 	int revents = pfd->revents;
889b041ccb2Sratchov 
890b041ccb2Sratchov 	if (!hdl->sio.started)
891b041ccb2Sratchov 		return pfd->revents;
892b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
893b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_PERROR, &xrun) < 0) {
894b041ccb2Sratchov 			DPERROR("sio_sun_revents: PERROR");
895b041ccb2Sratchov 			hdl->sio.eof = 1;
896b041ccb2Sratchov 			return POLLHUP;
897b041ccb2Sratchov 		}
898b041ccb2Sratchov 		doerr = xrun - hdl->oerr;
899b041ccb2Sratchov 		hdl->oerr = xrun;
900b041ccb2Sratchov 		if (!(hdl->sio.mode & SIO_REC))
901b041ccb2Sratchov 			dierr = doerr;
902b041ccb2Sratchov 	}
903b041ccb2Sratchov 	if (hdl->sio.mode & SIO_REC) {
904b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) {
905b041ccb2Sratchov 			DPERROR("sio_sun_revents: RERROR");
906b041ccb2Sratchov 			hdl->sio.eof = 1;
907b041ccb2Sratchov 			return POLLHUP;
908b041ccb2Sratchov 		}
909b041ccb2Sratchov 		dierr = xrun - hdl->ierr;
910b041ccb2Sratchov 		hdl->ierr = xrun;
911b041ccb2Sratchov 		if (!(hdl->sio.mode & SIO_PLAY))
912b041ccb2Sratchov 			doerr = dierr;
913b041ccb2Sratchov 	}
914b041ccb2Sratchov 	hdl->offset += doerr - dierr;
915b041ccb2Sratchov 	dmove = dierr > doerr ? dierr : doerr;
916b041ccb2Sratchov 	hdl->idelta -= dmove;
917b041ccb2Sratchov 	hdl->odelta -= dmove;
918b041ccb2Sratchov 
919b041ccb2Sratchov 	if ((revents & POLLOUT) && (hdl->sio.mode & SIO_PLAY)) {
920b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) {
921b041ccb2Sratchov 			DPERROR("sio_sun_revents: GETOOFFS");
922b041ccb2Sratchov 			hdl->sio.eof = 1;
923b041ccb2Sratchov 			return POLLHUP;
924b041ccb2Sratchov 		}
925b041ccb2Sratchov 		delta = (ao.samples - hdl->obytes) / hdl->obpf;
926b041ccb2Sratchov 		hdl->obytes = ao.samples;
927b041ccb2Sratchov 		hdl->odelta += delta;
928b041ccb2Sratchov 		if (!(hdl->sio.mode & SIO_REC))
929b041ccb2Sratchov 			hdl->idelta += delta;
930b041ccb2Sratchov 	}
931b041ccb2Sratchov 	if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) {
932b041ccb2Sratchov 		if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) {
933b041ccb2Sratchov 			DPERROR("sio_sun_revents: GETIOFFS");
934b041ccb2Sratchov 			hdl->sio.eof = 1;
935b041ccb2Sratchov 			return POLLHUP;
936b041ccb2Sratchov 		}
937b041ccb2Sratchov 		delta = (ao.samples - hdl->ibytes) / hdl->ibpf;
938b041ccb2Sratchov 		hdl->ibytes = ao.samples;
939b041ccb2Sratchov 		hdl->idelta += delta;
940b041ccb2Sratchov 		if (!(hdl->sio.mode & SIO_PLAY))
941b041ccb2Sratchov 			hdl->odelta += delta;
942b041ccb2Sratchov 	}
943b041ccb2Sratchov 	delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
944b041ccb2Sratchov 	if (delta > 0) {
945b041ccb2Sratchov 		sio_onmove_cb(&hdl->sio, delta);
946b041ccb2Sratchov 		hdl->idelta -= delta;
947b041ccb2Sratchov 		hdl->odelta -= delta;
948b041ccb2Sratchov 	}
949b041ccb2Sratchov 
950b041ccb2Sratchov 	/*
951b041ccb2Sratchov 	 * drop recorded samples or insert silence to play
952b041ccb2Sratchov 	 * right now to adjust revents, and avoid busy loops
953b041ccb2Sratchov 	 * programs
954b041ccb2Sratchov 	 */
955b041ccb2Sratchov 	if (hdl->filling)
956b041ccb2Sratchov 		revents |= POLLOUT;
957b041ccb2Sratchov 	if ((hdl->sio.mode & SIO_PLAY) && !sio_sun_wsil(hdl))
958b041ccb2Sratchov 		revents &= ~POLLOUT;
959b041ccb2Sratchov 	if ((hdl->sio.mode & SIO_REC) && !sio_sun_rdrop(hdl))
960b041ccb2Sratchov 		revents &= ~POLLIN;
961b041ccb2Sratchov 	return revents;
962b041ccb2Sratchov }
963