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