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