xref: /openbsd-src/lib/libsndio/sio_sun.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
1*2c53affbSjmc /*	$OpenBSD: sio_sun.c,v 1.30 2022/12/27 17:10:07 jmc 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 #include <sys/types.h>
19b041ccb2Sratchov #include <sys/ioctl.h>
20b041ccb2Sratchov #include <sys/audioio.h>
21b041ccb2Sratchov 
22b041ccb2Sratchov #include <errno.h>
23b041ccb2Sratchov #include <fcntl.h>
24b041ccb2Sratchov #include <limits.h>
25b041ccb2Sratchov #include <poll.h>
26b041ccb2Sratchov #include <stdio.h>
27b041ccb2Sratchov #include <stdlib.h>
28b041ccb2Sratchov #include <string.h>
29b041ccb2Sratchov #include <unistd.h>
30b041ccb2Sratchov 
3182bfc72bSratchov #include "debug.h"
32b041ccb2Sratchov #include "sio_priv.h"
33b041ccb2Sratchov 
34130e1f1bSratchov #define DEVPATH_PREFIX	"/dev/audio"
3528996f62Sratchov #define DEVPATH_MAX 	(1 +		\
3628996f62Sratchov 	sizeof(DEVPATH_PREFIX) - 1 +	\
3728996f62Sratchov 	sizeof(int) * 3)
3828996f62Sratchov 
39b041ccb2Sratchov struct sio_sun_hdl {
40b041ccb2Sratchov 	struct sio_hdl sio;
41b041ccb2Sratchov 	int fd;
42b041ccb2Sratchov 	int filling;
437207b069Sratchov 	unsigned int ibpf, obpf;	/* bytes per frame */
447207b069Sratchov 	unsigned int ibytes, obytes;	/* bytes the hw transferred */
457207b069Sratchov 	unsigned int ierr, oerr;	/* frames the hw dropped */
46b041ccb2Sratchov 	int idelta, odelta;		/* position reported to client */
47b041ccb2Sratchov };
48b041ccb2Sratchov 
49b041ccb2Sratchov static void sio_sun_close(struct sio_hdl *);
50b041ccb2Sratchov static int sio_sun_start(struct sio_hdl *);
51ec8a3410Sratchov static int sio_sun_flush(struct sio_hdl *);
52b041ccb2Sratchov static int sio_sun_setpar(struct sio_hdl *, struct sio_par *);
53b041ccb2Sratchov static int sio_sun_getpar(struct sio_hdl *, struct sio_par *);
54b041ccb2Sratchov static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *);
55b041ccb2Sratchov static size_t sio_sun_read(struct sio_hdl *, void *, size_t);
56b041ccb2Sratchov static size_t sio_sun_write(struct sio_hdl *, const void *, size_t);
57b041ccb2Sratchov static int sio_sun_nfds(struct sio_hdl *);
58b041ccb2Sratchov static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int);
59b041ccb2Sratchov static int sio_sun_revents(struct sio_hdl *, struct pollfd *);
60b041ccb2Sratchov 
61b041ccb2Sratchov static struct sio_ops sio_sun_ops = {
62b041ccb2Sratchov 	sio_sun_close,
63b041ccb2Sratchov 	sio_sun_setpar,
64b041ccb2Sratchov 	sio_sun_getpar,
65b041ccb2Sratchov 	sio_sun_getcap,
66b041ccb2Sratchov 	sio_sun_write,
67b041ccb2Sratchov 	sio_sun_read,
68b041ccb2Sratchov 	sio_sun_start,
69ec8a3410Sratchov 	NULL,
70ec8a3410Sratchov 	sio_sun_flush,
71b041ccb2Sratchov 	sio_sun_nfds,
72b041ccb2Sratchov 	sio_sun_pollfd,
73b041ccb2Sratchov 	sio_sun_revents,
74b041ccb2Sratchov 	NULL, /* setvol */
75b041ccb2Sratchov 	NULL, /* getvol */
76b041ccb2Sratchov };
77b041ccb2Sratchov 
78b041ccb2Sratchov static int
sio_sun_adjpar(struct sio_sun_hdl * hdl,struct audio_swpar * ap)797fa7c74cSratchov sio_sun_adjpar(struct sio_sun_hdl *hdl, struct audio_swpar *ap)
80b041ccb2Sratchov {
817fa7c74cSratchov 	if (hdl->sio.eof)
827fa7c74cSratchov 		return 0;
83df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_SETPAR, ap) == -1) {
847fa7c74cSratchov 		DPERROR("AUDIO_SETPAR");
857fa7c74cSratchov 		hdl->sio.eof = 1;
867fa7c74cSratchov 		return 0;
877fa7c74cSratchov 	}
88df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_GETPAR, ap) == -1) {
897fa7c74cSratchov 		DPERROR("AUDIO_GETPAR");
90b041ccb2Sratchov 		hdl->sio.eof = 1;
91b041ccb2Sratchov 		return 0;
92b041ccb2Sratchov 	}
93b041ccb2Sratchov 	return 1;
94b041ccb2Sratchov }
95b041ccb2Sratchov 
96b041ccb2Sratchov /*
97b041ccb2Sratchov  * try to set the device to the given parameters and check that the
98b041ccb2Sratchov  * device can use them; return 1 on success, 0 on failure or error
99b041ccb2Sratchov  */
100b041ccb2Sratchov static int
sio_sun_testpar(struct sio_sun_hdl * hdl,struct sio_enc * enc,unsigned int pchan,unsigned int rchan,unsigned int rate)1017fa7c74cSratchov sio_sun_testpar(struct sio_sun_hdl *hdl, struct sio_enc *enc,
1027207b069Sratchov     unsigned int pchan, unsigned int rchan, unsigned int rate)
103b041ccb2Sratchov {
1047fa7c74cSratchov 	struct audio_swpar ap;
105b041ccb2Sratchov 
1067fa7c74cSratchov 	AUDIO_INITPAR(&ap);
1077fa7c74cSratchov 	if (enc != NULL) {
1087fa7c74cSratchov 		ap.sig = enc->sig;
1097fa7c74cSratchov 		ap.bits = enc->bits;
1107fa7c74cSratchov 		ap.bps = enc->bps;
1117fa7c74cSratchov 		if (ap.bps > 1)
1127fa7c74cSratchov 			ap.le = enc->le;
1137fa7c74cSratchov 		if (ap.bps * 8 > ap.bits)
1147fa7c74cSratchov 			ap.msb = enc->msb;
115b041ccb2Sratchov 	}
116b041ccb2Sratchov 	if (rate)
1177fa7c74cSratchov 		ap.rate = rate;
118b041ccb2Sratchov 	if (pchan && (hdl->sio.mode & SIO_PLAY))
1197fa7c74cSratchov 		ap.pchan = pchan;
120b041ccb2Sratchov 	if (rchan && (hdl->sio.mode & SIO_REC))
1217fa7c74cSratchov 		ap.rchan = rchan;
1227fa7c74cSratchov 	if (!sio_sun_adjpar(hdl, &ap))
123b041ccb2Sratchov 		return 0;
1247fa7c74cSratchov 	if (pchan && ap.pchan != pchan)
125b041ccb2Sratchov 		return 0;
1267fa7c74cSratchov 	if (rchan && ap.rchan != rchan)
127b041ccb2Sratchov 		return 0;
1287fa7c74cSratchov 	if (rate && ap.rate != rate)
129b041ccb2Sratchov 		return 0;
1307fa7c74cSratchov 	if (enc) {
1317fa7c74cSratchov 		if (ap.sig != enc->sig)
132b041ccb2Sratchov 			return 0;
1337fa7c74cSratchov 		if (ap.bits != enc->bits)
134b041ccb2Sratchov 			return 0;
1357fa7c74cSratchov 		if (ap.bps != enc->bps)
1367fa7c74cSratchov 			return 0;
1377fa7c74cSratchov 		if (ap.bps > 1 && ap.le != enc->le)
1387fa7c74cSratchov 			return 0;
1397fa7c74cSratchov 		if (ap.bits < ap.bps * 8 && ap.msb != enc->msb)
140b041ccb2Sratchov 			return 0;
141b041ccb2Sratchov 	}
142b041ccb2Sratchov 	return 1;
143b041ccb2Sratchov }
144b041ccb2Sratchov 
145b041ccb2Sratchov /*
146b041ccb2Sratchov  * guess device capabilities
147b041ccb2Sratchov  */
148b041ccb2Sratchov static int
sio_sun_getcap(struct sio_hdl * sh,struct sio_cap * cap)149b041ccb2Sratchov sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap)
150b041ccb2Sratchov {
1517207b069Sratchov 	static unsigned int chans[] = {
152b041ccb2Sratchov 		1, 2, 4, 6, 8, 10, 12
153b041ccb2Sratchov 	};
1547207b069Sratchov 	static unsigned int rates[] = {
155b041ccb2Sratchov 		8000, 11025, 12000, 16000, 22050, 24000,
156b041ccb2Sratchov 		32000, 44100, 48000, 64000, 88200, 96000
157b041ccb2Sratchov 	};
1587fa7c74cSratchov 	static unsigned int encs[] = {
1597fa7c74cSratchov 		8, 16, 24, 32
1607fa7c74cSratchov 	};
161b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
1627fa7c74cSratchov 	struct audio_swpar savepar, ap;
1637fa7c74cSratchov 	unsigned int nconf = 0;
1647207b069Sratchov 	unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
1657207b069Sratchov 	unsigned int i, j, conf;
166b041ccb2Sratchov 
167df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_GETPAR, &savepar) == -1) {
1687fa7c74cSratchov 		DPERROR("AUDIO_GETPAR");
169b041ccb2Sratchov 		hdl->sio.eof = 1;
170b041ccb2Sratchov 		return 0;
171b041ccb2Sratchov 	}
1727fa7c74cSratchov 
1737fa7c74cSratchov 	/*
1747fa7c74cSratchov 	 * get a subset of supported encodings
1757fa7c74cSratchov 	 */
1767fa7c74cSratchov 	for (i = 0; i < sizeof(encs) / sizeof(encs[0]); i++) {
1777fa7c74cSratchov 		AUDIO_INITPAR(&ap);
1787fa7c74cSratchov 		ap.bits = encs[i];
1797fa7c74cSratchov 		ap.sig = (ap.bits > 8) ? 1 : 0;
1807fa7c74cSratchov 		if (!sio_sun_adjpar(hdl, &ap))
1817fa7c74cSratchov 			return 0;
1827fa7c74cSratchov 		if (ap.bits == encs[i]) {
1837fa7c74cSratchov 			cap->enc[i].sig = ap.sig;
1847fa7c74cSratchov 			cap->enc[i].bits = ap.bits;
1857fa7c74cSratchov 			cap->enc[i].le = ap.le;
1867fa7c74cSratchov 			cap->enc[i].bps = ap.bps;
1877fa7c74cSratchov 			cap->enc[i].msb = ap.msb;
1887fa7c74cSratchov 			enc_map |= 1 << i;
189b041ccb2Sratchov 		}
190b041ccb2Sratchov 	}
191b041ccb2Sratchov 
192b041ccb2Sratchov 	/*
193b041ccb2Sratchov 	 * fill channels
194b041ccb2Sratchov 	 *
195b041ccb2Sratchov 	 * for now we're lucky: all kernel devices assume that the
196b041ccb2Sratchov 	 * number of channels and the encoding are independent so we can
197b041ccb2Sratchov 	 * use the current encoding and try various channels.
198b041ccb2Sratchov 	 */
199b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
2007fa7c74cSratchov 		for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
2017fa7c74cSratchov 			AUDIO_INITPAR(&ap);
2027fa7c74cSratchov 			ap.pchan = chans[i];
2037fa7c74cSratchov 			if (!sio_sun_adjpar(hdl, &ap))
2047fa7c74cSratchov 				return 0;
2057fa7c74cSratchov 			if (ap.pchan == chans[i]) {
2067fa7c74cSratchov 				cap->pchan[i] = chans[i];
207b041ccb2Sratchov 				pchan_map |= (1 << i);
208b041ccb2Sratchov 			}
209b041ccb2Sratchov 		}
2107fa7c74cSratchov 	}
211b041ccb2Sratchov 	if (hdl->sio.mode & SIO_REC) {
2127fa7c74cSratchov 		for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
2137fa7c74cSratchov 			AUDIO_INITPAR(&ap);
2147fa7c74cSratchov 			ap.pchan = chans[i];
2157fa7c74cSratchov 			if (!sio_sun_adjpar(hdl, &ap))
2167fa7c74cSratchov 				return 0;
2177fa7c74cSratchov 			if (ap.rchan == chans[i]) {
2187fa7c74cSratchov 				cap->rchan[i] = chans[i];
219b041ccb2Sratchov 				rchan_map |= (1 << i);
220b041ccb2Sratchov 			}
221b041ccb2Sratchov 		}
2227fa7c74cSratchov 	}
223b041ccb2Sratchov 
224b041ccb2Sratchov 	/*
225b041ccb2Sratchov 	 * fill rates
226b041ccb2Sratchov 	 *
227b041ccb2Sratchov 	 * rates are not independent from other parameters (eg. on
228b041ccb2Sratchov 	 * uaudio devices), so certain rates may not be allowed with
229b041ccb2Sratchov 	 * certain encodings. We have to check rates for all encodings
230b041ccb2Sratchov 	 */
2317fa7c74cSratchov 	for (j = 0; j < sizeof(encs) / sizeof(encs[0]); j++) {
232b041ccb2Sratchov 		rate_map = 0;
2337fa7c74cSratchov 		if ((enc_map & (1 << j)) == 0)
2347fa7c74cSratchov 			continue;
2357fa7c74cSratchov 		for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
2367fa7c74cSratchov 			if (sio_sun_testpar(hdl,
2377fa7c74cSratchov 				&cap->enc[j], 0, 0, rates[i])) {
2387fa7c74cSratchov 				cap->rate[i] = rates[i];
239b041ccb2Sratchov 				rate_map |= (1 << i);
240b041ccb2Sratchov 			}
2417fa7c74cSratchov 		}
242b041ccb2Sratchov 		for (conf = 0; conf < nconf; conf++) {
243b041ccb2Sratchov 			if (cap->confs[conf].rate == rate_map) {
244b041ccb2Sratchov 				cap->confs[conf].enc |= (1 << j);
245b041ccb2Sratchov 				break;
246b041ccb2Sratchov 			}
247b041ccb2Sratchov 		}
248b041ccb2Sratchov 		if (conf == nconf) {
249b041ccb2Sratchov 			if (nconf == SIO_NCONF)
250b041ccb2Sratchov 				break;
251b041ccb2Sratchov 			cap->confs[nconf].enc = (1 << j);
252b041ccb2Sratchov 			cap->confs[nconf].pchan = pchan_map;
253b041ccb2Sratchov 			cap->confs[nconf].rchan = rchan_map;
254b041ccb2Sratchov 			cap->confs[nconf].rate = rate_map;
255b041ccb2Sratchov 			nconf++;
256b041ccb2Sratchov 		}
257b041ccb2Sratchov 	}
258b041ccb2Sratchov 	cap->nconf = nconf;
2597fa7c74cSratchov 
260df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_SETPAR, &savepar) == -1) {
2617fa7c74cSratchov 		DPERROR("AUDIO_SETPAR");
2627fa7c74cSratchov 		hdl->sio.eof = 1;
263b041ccb2Sratchov 		return 0;
2647fa7c74cSratchov 	}
265b041ccb2Sratchov 	return 1;
266b041ccb2Sratchov }
267b041ccb2Sratchov 
268bde5d162Sratchov int
sio_sun_getfd(const char * str,unsigned int mode,int nbio)269413b52fdSratchov sio_sun_getfd(const char *str, unsigned int mode, int nbio)
270b041ccb2Sratchov {
271163486f8Sratchov 	const char *p;
27228996f62Sratchov 	char path[DEVPATH_MAX];
273b44cb2caSratchov 	unsigned int devnum;
274413b52fdSratchov 	int fd, flags;
275b041ccb2Sratchov 
276bde5d162Sratchov #ifdef DEBUG
277bde5d162Sratchov 	_sndio_debug_init();
278bde5d162Sratchov #endif
279163486f8Sratchov 	p = _sndio_parsetype(str, "rsnd");
280163486f8Sratchov 	if (p == NULL) {
281163486f8Sratchov 		DPRINTF("sio_sun_getfd: %s: \"rsnd\" expected\n", str);
282413b52fdSratchov 		return -1;
283b3956098Sratchov 	}
284163486f8Sratchov 	switch (*p) {
285163486f8Sratchov 	case '/':
286163486f8Sratchov 		p++;
287163486f8Sratchov 		break;
288163486f8Sratchov 	default:
289163486f8Sratchov 		DPRINTF("sio_sun_getfd: %s: '/' expected\n", str);
290163486f8Sratchov 		return -1;
291163486f8Sratchov 	}
292163486f8Sratchov 	p = _sndio_parsenum(p, &devnum, 255);
293163486f8Sratchov 	if (p == NULL || *p != '\0') {
294163486f8Sratchov 		DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str);
295413b52fdSratchov 		return -1;
296b44cb2caSratchov 	}
297b44cb2caSratchov 	snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
298b041ccb2Sratchov 	if (mode == (SIO_PLAY | SIO_REC))
299b041ccb2Sratchov 		flags = O_RDWR;
300b041ccb2Sratchov 	else
301b041ccb2Sratchov 		flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
302df69c215Sderaadt 	while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) == -1) {
303b041ccb2Sratchov 		if (errno == EINTR)
304b041ccb2Sratchov 			continue;
305b041ccb2Sratchov 		DPERROR(path);
306413b52fdSratchov 		return -1;
307b041ccb2Sratchov 	}
308413b52fdSratchov 	return fd;
309413b52fdSratchov }
310413b52fdSratchov 
311bde5d162Sratchov struct sio_hdl *
sio_sun_fdopen(int fd,unsigned int mode,int nbio)312413b52fdSratchov sio_sun_fdopen(int fd, unsigned int mode, int nbio)
313413b52fdSratchov {
314413b52fdSratchov 	struct sio_sun_hdl *hdl;
315413b52fdSratchov 
316bde5d162Sratchov #ifdef DEBUG
317bde5d162Sratchov 	_sndio_debug_init();
318bde5d162Sratchov #endif
319413b52fdSratchov 	hdl = malloc(sizeof(struct sio_sun_hdl));
320413b52fdSratchov 	if (hdl == NULL)
321413b52fdSratchov 		return NULL;
322413b52fdSratchov 	_sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
323b041ccb2Sratchov 
324b041ccb2Sratchov 	/*
325b041ccb2Sratchov 	 * pause the device
326b041ccb2Sratchov 	 */
327df69c215Sderaadt 	if (ioctl(fd, AUDIO_STOP) == -1) {
3287fa7c74cSratchov 		DPERROR("AUDIO_STOP");
329b041ccb2Sratchov 		free(hdl);
330b041ccb2Sratchov 		return NULL;
331b041ccb2Sratchov 	}
3327fa7c74cSratchov 	hdl->fd = fd;
3337fa7c74cSratchov 	hdl->filling = 0;
3347fa7c74cSratchov 	return (struct sio_hdl *)hdl;
3357fa7c74cSratchov }
336b041ccb2Sratchov 
337413b52fdSratchov struct sio_hdl *
_sio_sun_open(const char * str,unsigned int mode,int nbio)338413b52fdSratchov _sio_sun_open(const char *str, unsigned int mode, int nbio)
339413b52fdSratchov {
340413b52fdSratchov 	struct sio_hdl *hdl;
341413b52fdSratchov 	int fd;
342413b52fdSratchov 
343413b52fdSratchov 	fd = sio_sun_getfd(str, mode, nbio);
344df69c215Sderaadt 	if (fd == -1)
345413b52fdSratchov 		return NULL;
346413b52fdSratchov 	hdl = sio_sun_fdopen(fd, mode, nbio);
347413b52fdSratchov 	if (hdl != NULL)
348413b52fdSratchov 		return hdl;
349df69c215Sderaadt 	while (close(fd) == -1 && errno == EINTR)
350413b52fdSratchov 		; /* retry */
351413b52fdSratchov 	return NULL;
352413b52fdSratchov }
353413b52fdSratchov 
354b041ccb2Sratchov static void
sio_sun_close(struct sio_hdl * sh)355b041ccb2Sratchov sio_sun_close(struct sio_hdl *sh)
356b041ccb2Sratchov {
357b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
358b041ccb2Sratchov 
359df69c215Sderaadt 	while (close(hdl->fd) == -1 && errno == EINTR)
360b041ccb2Sratchov 		; /* retry */
361b041ccb2Sratchov 	free(hdl);
362b041ccb2Sratchov }
363b041ccb2Sratchov 
364b041ccb2Sratchov static int
sio_sun_start(struct sio_hdl * sh)365b041ccb2Sratchov sio_sun_start(struct sio_hdl *sh)
366b041ccb2Sratchov {
367b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
368b041ccb2Sratchov 
369e6877796Sratchov 	hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
370e6877796Sratchov 	hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
371b041ccb2Sratchov 	hdl->ibytes = 0;
372b041ccb2Sratchov 	hdl->obytes = 0;
373b041ccb2Sratchov 	hdl->ierr = 0;
374b041ccb2Sratchov 	hdl->oerr = 0;
375b041ccb2Sratchov 	hdl->idelta = 0;
376b041ccb2Sratchov 	hdl->odelta = 0;
377b041ccb2Sratchov 
378b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
379b041ccb2Sratchov 		/*
3807fa7c74cSratchov 		 * keep the device paused and let sio_sun_pollfd() trigger the
381b041ccb2Sratchov 		 * start later, to avoid buffer underruns
382b041ccb2Sratchov 		 */
383b041ccb2Sratchov 		hdl->filling = 1;
384b041ccb2Sratchov 	} else {
385b041ccb2Sratchov 		/*
386b041ccb2Sratchov 		 * no play buffers to fill, start now!
387b041ccb2Sratchov 		 */
388df69c215Sderaadt 		if (ioctl(hdl->fd, AUDIO_START) == -1) {
3897fa7c74cSratchov 			DPERROR("AUDIO_START");
390b041ccb2Sratchov 			hdl->sio.eof = 1;
391b041ccb2Sratchov 			return 0;
392b041ccb2Sratchov 		}
393d418f94bSratchov 		_sio_onmove_cb(&hdl->sio, 0);
394b041ccb2Sratchov 	}
395b041ccb2Sratchov 	return 1;
396b041ccb2Sratchov }
397b041ccb2Sratchov 
398b041ccb2Sratchov static int
sio_sun_flush(struct sio_hdl * sh)399ec8a3410Sratchov sio_sun_flush(struct sio_hdl *sh)
400b041ccb2Sratchov {
401b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
402b041ccb2Sratchov 
4037fa7c74cSratchov 	if (hdl->filling) {
4047fa7c74cSratchov 		hdl->filling = 0;
4057fa7c74cSratchov 		return 1;
406b041ccb2Sratchov 	}
407df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_STOP) == -1) {
4087fa7c74cSratchov 		DPERROR("AUDIO_STOP");
409b041ccb2Sratchov 		hdl->sio.eof = 1;
410b041ccb2Sratchov 		return 0;
411b041ccb2Sratchov 	}
412b041ccb2Sratchov 	return 1;
413b041ccb2Sratchov }
414b041ccb2Sratchov 
415b041ccb2Sratchov static int
sio_sun_setpar(struct sio_hdl * sh,struct sio_par * par)416b041ccb2Sratchov sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
417b041ccb2Sratchov {
418b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
4197fa7c74cSratchov 	struct audio_swpar ap;
420b041ccb2Sratchov 
4217fa7c74cSratchov 	AUDIO_INITPAR(&ap);
4227fa7c74cSratchov 	ap.sig = par->sig;
4237fa7c74cSratchov 	ap.le = par->le;
4247fa7c74cSratchov 	ap.bits = par->bits;
4257fa7c74cSratchov 	ap.bps = par->bps;
4267fa7c74cSratchov 	ap.msb = par->msb;
4277fa7c74cSratchov 	ap.rate = par->rate;
428b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY)
4297fa7c74cSratchov 		ap.pchan = par->pchan;
4307fa7c74cSratchov 	if (hdl->sio.mode & SIO_REC)
4317fa7c74cSratchov 		ap.rchan = par->rchan;
4327fa7c74cSratchov 	if (par->round != ~0U && par->appbufsz != ~0U) {
4337fa7c74cSratchov 		ap.round = par->round;
4347fa7c74cSratchov 		ap.nblks = par->appbufsz / par->round;
4357fa7c74cSratchov 	} else if (par->round != ~0U) {
4367fa7c74cSratchov 		ap.round = par->round;
4377fa7c74cSratchov 		ap.nblks = 2;
4387fa7c74cSratchov 	} else if (par->appbufsz != ~0U) {
4397fa7c74cSratchov 		ap.round = par->appbufsz / 2;
4407fa7c74cSratchov 		ap.nblks = 2;
4417fa7c74cSratchov 	}
442df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_SETPAR, &ap) == -1) {
4437fa7c74cSratchov 		DPERROR("AUDIO_SETPAR");
444b041ccb2Sratchov 		hdl->sio.eof = 1;
445b041ccb2Sratchov 		return 0;
446b041ccb2Sratchov 	}
447b041ccb2Sratchov 	return 1;
448b041ccb2Sratchov }
449b041ccb2Sratchov 
450b041ccb2Sratchov static int
sio_sun_getpar(struct sio_hdl * sh,struct sio_par * par)451b041ccb2Sratchov sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
452b041ccb2Sratchov {
453b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
4547fa7c74cSratchov 	struct audio_swpar ap;
455b041ccb2Sratchov 
456df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_GETPAR, &ap) == -1) {
4577fa7c74cSratchov 		DPERROR("AUDIO_GETPAR");
458b041ccb2Sratchov 		hdl->sio.eof = 1;
459b041ccb2Sratchov 		return 0;
460b041ccb2Sratchov 	}
4617fa7c74cSratchov 	par->sig = ap.sig;
4627fa7c74cSratchov 	par->le = ap.le;
4637fa7c74cSratchov 	par->bits = ap.bits;
4647fa7c74cSratchov 	par->bps = ap.bps;
4657fa7c74cSratchov 	par->msb = ap.msb;
4667fa7c74cSratchov 	par->rate = ap.rate;
4677fa7c74cSratchov 	par->pchan = ap.pchan;
4687fa7c74cSratchov 	par->rchan = ap.rchan;
4697fa7c74cSratchov 	par->round = ap.round;
4707fa7c74cSratchov 	par->appbufsz = par->bufsz = ap.nblks * ap.round;
47117645762Sratchov 	par->xrun = SIO_IGNORE;
472b041ccb2Sratchov 	return 1;
473b041ccb2Sratchov }
474b041ccb2Sratchov 
475b041ccb2Sratchov static size_t
sio_sun_read(struct sio_hdl * sh,void * buf,size_t len)476b041ccb2Sratchov sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
477b041ccb2Sratchov {
478b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
479b041ccb2Sratchov 	ssize_t n;
480b041ccb2Sratchov 
481df69c215Sderaadt 	while ((n = read(hdl->fd, buf, len)) == -1) {
482b041ccb2Sratchov 		if (errno == EINTR)
483b041ccb2Sratchov 			continue;
484b041ccb2Sratchov 		if (errno != EAGAIN) {
485b041ccb2Sratchov 			DPERROR("sio_sun_read: read");
486b041ccb2Sratchov 			hdl->sio.eof = 1;
487b041ccb2Sratchov 		}
488b041ccb2Sratchov 		return 0;
489b041ccb2Sratchov 	}
490b041ccb2Sratchov 	if (n == 0) {
491b041ccb2Sratchov 		DPRINTF("sio_sun_read: eof\n");
492b041ccb2Sratchov 		hdl->sio.eof = 1;
493b041ccb2Sratchov 		return 0;
494b041ccb2Sratchov 	}
495b041ccb2Sratchov 	return n;
496b041ccb2Sratchov }
497b041ccb2Sratchov 
498b041ccb2Sratchov static size_t
sio_sun_write(struct sio_hdl * sh,const void * buf,size_t len)499b041ccb2Sratchov sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
500b041ccb2Sratchov {
501b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
502b041ccb2Sratchov 	const unsigned char *data = buf;
503b041ccb2Sratchov 	ssize_t n, todo;
504b041ccb2Sratchov 
505b041ccb2Sratchov 	todo = len;
506df69c215Sderaadt 	while ((n = write(hdl->fd, data, todo)) == -1) {
507b041ccb2Sratchov 		if (errno == EINTR)
508b041ccb2Sratchov 			continue;
509b041ccb2Sratchov 		if (errno != EAGAIN) {
510b041ccb2Sratchov 			DPERROR("sio_sun_write: write");
511b041ccb2Sratchov 			hdl->sio.eof = 1;
512b041ccb2Sratchov 		}
513b041ccb2Sratchov 		return 0;
514b041ccb2Sratchov 	}
515b041ccb2Sratchov 	return n;
516b041ccb2Sratchov }
517b041ccb2Sratchov 
518b041ccb2Sratchov static int
sio_sun_nfds(struct sio_hdl * hdl)519b041ccb2Sratchov sio_sun_nfds(struct sio_hdl *hdl)
520b041ccb2Sratchov {
521b041ccb2Sratchov 	return 1;
522b041ccb2Sratchov }
523b041ccb2Sratchov 
524b041ccb2Sratchov static int
sio_sun_pollfd(struct sio_hdl * sh,struct pollfd * pfd,int events)525b041ccb2Sratchov sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
526b041ccb2Sratchov {
527b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
528b041ccb2Sratchov 
529b041ccb2Sratchov 	pfd->fd = hdl->fd;
530b041ccb2Sratchov 	pfd->events = events;
5317fa7c74cSratchov 	if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz *
5327fa7c74cSratchov 		hdl->sio.par.pchan * hdl->sio.par.bps) {
5337fa7c74cSratchov 		hdl->filling = 0;
534df69c215Sderaadt 		if (ioctl(hdl->fd, AUDIO_START) == -1) {
5357fa7c74cSratchov 			DPERROR("AUDIO_START");
5367fa7c74cSratchov 			hdl->sio.eof = 1;
5377fa7c74cSratchov 			return 0;
5387fa7c74cSratchov 		}
5397fa7c74cSratchov 		_sio_onmove_cb(&hdl->sio, 0);
5407fa7c74cSratchov 	}
541b041ccb2Sratchov 	return 1;
542b041ccb2Sratchov }
543b041ccb2Sratchov 
544b041ccb2Sratchov int
sio_sun_revents(struct sio_hdl * sh,struct pollfd * pfd)545b041ccb2Sratchov sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
546b041ccb2Sratchov {
547b041ccb2Sratchov 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
5489d7d964fSratchov 	struct audio_pos ap;
5499d7d964fSratchov 	int dierr = 0, doerr = 0, offset, delta;
550b041ccb2Sratchov 	int revents = pfd->revents;
551b041ccb2Sratchov 
5527fa7c74cSratchov 	if ((pfd->revents & POLLHUP) ||
5537fa7c74cSratchov 	    (pfd->revents & (POLLIN | POLLOUT)) == 0)
554b041ccb2Sratchov 		return pfd->revents;
555df69c215Sderaadt 	if (ioctl(hdl->fd, AUDIO_GETPOS, &ap) == -1) {
5569d7d964fSratchov 		DPERROR("sio_sun_revents: GETPOS");
557e7d2a37cSratchov 		hdl->sio.eof = 1;
558e7d2a37cSratchov 		return POLLHUP;
559e7d2a37cSratchov 	}
560b041ccb2Sratchov 	if (hdl->sio.mode & SIO_PLAY) {
5619d7d964fSratchov 		delta = (ap.play_pos - hdl->obytes) / hdl->obpf;
5629d7d964fSratchov 		doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
5639d7d964fSratchov 		hdl->obytes = ap.play_pos;
5649d7d964fSratchov 		hdl->oerr = ap.play_xrun;
5659d7d964fSratchov 		hdl->odelta += delta;
5669d7d964fSratchov 		if (!(hdl->sio.mode & SIO_REC)) {
5679d7d964fSratchov 			hdl->idelta += delta;
568b041ccb2Sratchov 			dierr = doerr;
5699d7d964fSratchov 		}
570ea382012Sratchov 		if (doerr > 0)
571e791f9efSratchov 			DPRINTFN(2, "play xrun %d\n", doerr);
572b041ccb2Sratchov 	}
573b041ccb2Sratchov 	if (hdl->sio.mode & SIO_REC) {
5749d7d964fSratchov 		delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf;
5759d7d964fSratchov 		dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf;
5769d7d964fSratchov 		hdl->ibytes = ap.rec_pos;
5779d7d964fSratchov 		hdl->ierr = ap.rec_xrun;
5789d7d964fSratchov 		hdl->idelta += delta;
5799d7d964fSratchov 		if (!(hdl->sio.mode & SIO_PLAY)) {
5809d7d964fSratchov 			hdl->odelta += delta;
581b041ccb2Sratchov 			doerr = dierr;
5829d7d964fSratchov 		}
583ea382012Sratchov 		if (dierr > 0)
584e791f9efSratchov 			DPRINTFN(2, "rec xrun %d\n", dierr);
585b041ccb2Sratchov 	}
586e6877796Sratchov 
587e6877796Sratchov 	/*
5889d7d964fSratchov 	 * GETPOS reports positions including xruns,
589*2c53affbSjmc 	 * so we have to subtract to get the real position
590e6877796Sratchov 	 */
591e6877796Sratchov 	hdl->idelta -= dierr;
592e6877796Sratchov 	hdl->odelta -= doerr;
593e6877796Sratchov 
594ea382012Sratchov 	offset = doerr - dierr;
595ea382012Sratchov 	if (offset > 0) {
596ea382012Sratchov 		hdl->sio.rdrop += offset * hdl->ibpf;
597e6877796Sratchov 		hdl->idelta -= offset;
598e791f9efSratchov 		DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr);
599ea382012Sratchov 	} else if (offset < 0) {
600ea382012Sratchov 		hdl->sio.wsil += -offset * hdl->obpf;
6011664e8c8Sratchov 		hdl->odelta -= -offset;
602e791f9efSratchov 		DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
603ea382012Sratchov 	}
604b041ccb2Sratchov 
605b041ccb2Sratchov 	delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
606b041ccb2Sratchov 	if (delta > 0) {
607d418f94bSratchov 		_sio_onmove_cb(&hdl->sio, delta);
608b041ccb2Sratchov 		hdl->idelta -= delta;
609b041ccb2Sratchov 		hdl->odelta -= delta;
610b041ccb2Sratchov 	}
611b041ccb2Sratchov 	return revents;
612b041ccb2Sratchov }
613