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