xref: /openbsd/lib/libsndio/sio_sun.c (revision 28996f62)
1 /*	$OpenBSD: sio_sun.c,v 1.18 2015/10/02 09:45:26 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 struct sio_hdl *
332 _sio_sun_open(const char *str, unsigned int mode, int nbio)
333 {
334 	int fd, flags, fullduplex;
335 	struct audio_info aui;
336 	struct sio_sun_hdl *hdl;
337 	struct sio_par par;
338 	char path[DEVPATH_MAX];
339 
340 	switch (*str) {
341 	case '/':
342 		str++;
343 		break;
344 	default:
345 		DPRINTF("_sio_sun_open: %s: '/<devnum>' expected\n", str);
346 		return NULL;
347 	}
348 	hdl = malloc(sizeof(struct sio_sun_hdl));
349 	if (hdl == NULL)
350 		return NULL;
351 	_sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
352 
353 	snprintf(path, sizeof(path), DEVPATH_PREFIX "%s", str);
354 	if (mode == (SIO_PLAY | SIO_REC))
355 		flags = O_RDWR;
356 	else
357 		flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
358 
359 	while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) < 0) {
360 		if (errno == EINTR)
361 			continue;
362 		DPERROR(path);
363 		goto bad_free;
364 	}
365 
366 	/*
367 	 * pause the device
368 	 */
369 	AUDIO_INITINFO(&aui);
370 	if (mode & SIO_PLAY)
371 		aui.play.pause = 1;
372 	if (mode & SIO_REC)
373 		aui.record.pause = 1;
374 	if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) {
375 		DPERROR("sio_open_sun: setinfo");
376 		goto bad_close;
377 	}
378 	/*
379 	 * If both play and record are requested then
380 	 * set full duplex mode.
381 	 */
382 	if (mode == (SIO_PLAY | SIO_REC)) {
383 		fullduplex = 1;
384 		if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) {
385 			DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path);
386 			goto bad_close;
387 		}
388 	}
389 	hdl->fd = fd;
390 
391 	/*
392 	 * Default parameters may not be compatible with libsndio (eg. mulaw
393 	 * encodings, different playback and recording parameters, etc...), so
394 	 * set parameters to a random value. If the requested parameters are
395 	 * not supported by the device, then sio_setpar() will pick supported
396 	 * ones.
397 	 */
398 	sio_initpar(&par);
399 	par.rate = 48000;
400 	par.le = SIO_LE_NATIVE;
401 	par.sig = 1;
402 	par.bits = 16;
403 	par.appbufsz = 1200;
404 	if (!sio_setpar(&hdl->sio, &par))
405 		goto bad_close;
406 	return (struct sio_hdl *)hdl;
407  bad_close:
408 	while (close(fd) < 0 && errno == EINTR)
409 		; /* retry */
410  bad_free:
411 	free(hdl);
412 	return NULL;
413 }
414 
415 static void
416 sio_sun_close(struct sio_hdl *sh)
417 {
418 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
419 
420 	while (close(hdl->fd) < 0 && errno == EINTR)
421 		; /* retry */
422 	free(hdl);
423 }
424 
425 static int
426 sio_sun_start(struct sio_hdl *sh)
427 {
428 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
429 	struct audio_info aui;
430 
431 	hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
432 	hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
433 	hdl->ibytes = 0;
434 	hdl->obytes = 0;
435 	hdl->ierr = 0;
436 	hdl->oerr = 0;
437 	hdl->idelta = 0;
438 	hdl->odelta = 0;
439 
440 	if (hdl->sio.mode & SIO_PLAY) {
441 		/*
442 		 * keep the device paused and let sio_sun_write() trigger the
443 		 * start later, to avoid buffer underruns
444 		 */
445 		hdl->filling = 1;
446 	} else {
447 		/*
448 		 * no play buffers to fill, start now!
449 		 */
450 		AUDIO_INITINFO(&aui);
451 		if (hdl->sio.mode & SIO_REC)
452 			aui.record.pause = 0;
453 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
454 			DPERROR("sio_sun_start: setinfo");
455 			hdl->sio.eof = 1;
456 			return 0;
457 		}
458 		hdl->filling = 0;
459 		_sio_onmove_cb(&hdl->sio, 0);
460 	}
461 	return 1;
462 }
463 
464 static int
465 sio_sun_stop(struct sio_hdl *sh)
466 {
467 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
468 	struct audio_info aui;
469 	int mode;
470 
471 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
472 		DPERROR("sio_sun_stop: getinfo");
473 		hdl->sio.eof = 1;
474 		return 0;
475 	}
476 	mode = aui.mode;
477 
478 	/*
479 	 * there's no way to drain the device without blocking, so just
480 	 * stop it until the kernel driver get fixed
481 	 */
482 	AUDIO_INITINFO(&aui);
483 	aui.mode = 0;
484 	if (hdl->sio.mode & SIO_PLAY)
485 		aui.play.pause = 1;
486 	if (hdl->sio.mode & SIO_REC)
487 		aui.record.pause = 1;
488 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
489 		DPERROR("sio_sun_stop: setinfo1");
490 		hdl->sio.eof = 1;
491 		return 0;
492 	}
493 	AUDIO_INITINFO(&aui);
494 	aui.mode = mode;
495 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
496 		DPERROR("sio_sun_stop: setinfo2");
497 		hdl->sio.eof = 1;
498 		return 0;
499 	}
500 	return 1;
501 }
502 
503 static int
504 sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
505 {
506 #define NRETRIES 8
507 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
508 	struct audio_info aui;
509 	unsigned int i, infr, ibpf, onfr, obpf;
510 	unsigned int bufsz, round;
511 	unsigned int rate, req_rate, prec, enc;
512 
513 	/*
514 	 * try to set parameters until the device accepts
515 	 * a common encoding and rate for play and record
516 	 */
517 	rate = par->rate;
518 	prec = par->bits;
519 	sio_sun_enctoinfo(hdl, &enc, par);
520 	for (i = 0;; i++) {
521 		if (i == NRETRIES) {
522 			DPRINTF("sio_sun_setpar: couldn't set parameters\n");
523 			hdl->sio.eof = 1;
524 			return 0;
525 		}
526 		AUDIO_INITINFO(&aui);
527 		if (hdl->sio.mode & SIO_PLAY) {
528 			aui.play.sample_rate = rate;
529 			aui.play.precision = prec;
530 			aui.play.encoding = enc;
531 			aui.play.channels = par->pchan;
532 		}
533 		if (hdl->sio.mode & SIO_REC) {
534 			aui.record.sample_rate = rate;
535 			aui.record.precision = prec;
536 			aui.record.encoding = enc;
537 			aui.record.channels = par->rchan;
538 		}
539 		DPRINTFN(2, "sio_sun_setpar: %i: trying pars = %u/%u/%u\n",
540 		    i, rate, prec, enc);
541 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) {
542 			DPERROR("sio_sun_setpar: setinfo(pars)");
543 			hdl->sio.eof = 1;
544 			return 0;
545 		}
546 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
547 			DPERROR("sio_sun_setpar: getinfo(pars)");
548 			hdl->sio.eof = 1;
549 			return 0;
550 		}
551 		enc = (hdl->sio.mode & SIO_REC) ?
552 		    aui.record.encoding : aui.play.encoding;
553 		switch (enc) {
554 		case AUDIO_ENCODING_SLINEAR_LE:
555 		case AUDIO_ENCODING_SLINEAR_BE:
556 		case AUDIO_ENCODING_ULINEAR_LE:
557 		case AUDIO_ENCODING_ULINEAR_BE:
558 		case AUDIO_ENCODING_SLINEAR:
559 		case AUDIO_ENCODING_ULINEAR:
560 			break;
561 		default:
562 			DPRINTF("sio_sun_setpar: couldn't set linear encoding\n");
563 			hdl->sio.eof = 1;
564 			return 0;
565 		}
566 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY))
567 			break;
568 		if (aui.play.sample_rate == aui.record.sample_rate &&
569 		    aui.play.precision == aui.record.precision &&
570 		    aui.play.encoding == aui.record.encoding)
571 			break;
572 		if (i < NRETRIES / 2) {
573 			rate = aui.play.sample_rate;
574 			prec = aui.play.precision;
575 			enc = aui.play.encoding;
576 		} else {
577 			rate = aui.record.sample_rate;
578 			prec = aui.record.precision;
579 			enc = aui.record.encoding;
580 		}
581 	}
582 
583 	/*
584 	 * If the rate that the hardware is using is different than
585 	 * the requested rate, scale buffer sizes so they will be the
586 	 * same time duration as what was requested.  This just gets
587 	 * the rates to use for scaling, that actual scaling is done
588 	 * later.
589 	 */
590 	rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate :
591 	    aui.play.sample_rate;
592 	req_rate = rate;
593 	if (par->rate && par->rate != ~0U)
594 		req_rate = par->rate;
595 
596 	/*
597 	 * if block size and buffer size are not both set then
598 	 * set the blocksize to half the buffer size
599 	 */
600 	bufsz = par->appbufsz;
601 	round = par->round;
602 	if (bufsz != ~0U) {
603 		bufsz = bufsz * rate / req_rate;
604 		if (round == ~0U)
605 			round = (bufsz + 1) / 2;
606 		else
607 			round = round * rate / req_rate;
608 	} else if (round != ~0U) {
609 		round = round * rate / req_rate;
610 		bufsz = round * 2;
611 	} else
612 		return 1;
613 
614 	/*
615 	 * get the play/record frame size in bytes
616 	 */
617 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
618 		DPERROR("sio_sun_setpar: GETINFO");
619 		hdl->sio.eof = 1;
620 		return 0;
621 	}
622 	ibpf = (hdl->sio.mode & SIO_REC) ?
623 	    aui.record.channels * aui.record.bps : 1;
624 	obpf = (hdl->sio.mode & SIO_PLAY) ?
625 	    aui.play.channels * aui.play.bps : 1;
626 
627 	DPRINTFN(2, "sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf);
628 
629 	/*
630 	 * try to set parameters until the device accepts
631 	 * a common block size for play and record
632 	 */
633 	for (i = 0; i < NRETRIES; i++) {
634 		AUDIO_INITINFO(&aui);
635 		aui.hiwat = (bufsz + round - 1) / round;
636 		aui.lowat = aui.hiwat;
637 		if (hdl->sio.mode & SIO_REC)
638 			aui.record.block_size = round * ibpf;
639 		if (hdl->sio.mode & SIO_PLAY)
640 			aui.play.block_size = round * obpf;
641 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
642 			DPERROR("sio_sun_setpar2: SETINFO");
643 			hdl->sio.eof = 1;
644 			return 0;
645 		}
646 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
647 			DPERROR("sio_sun_setpar2: GETINFO");
648 			hdl->sio.eof = 1;
649 			return 0;
650 		}
651 		infr = aui.record.block_size / ibpf;
652 		onfr = aui.play.block_size / obpf;
653 		DPRINTFN(2, "sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n",
654 		    i, round, infr, onfr);
655 
656 		/*
657 		 * if half-duplex or both block sizes match, we're done
658 		 */
659 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) {
660 			DPRINTFN(2, "sio_sun_setpar: blocksize ok\n");
661 			return 1;
662 		}
663 
664 		/*
665 		 * half of the retries, retry with the smaller value,
666 		 * then with the larger returned value
667 		 */
668 		if (i < NRETRIES / 2)
669 			round = infr < onfr ? infr : onfr;
670 		else
671 			round = infr < onfr ? onfr : infr;
672 	}
673 	DPRINTFN(2, "sio_sun_setpar: couldn't find a working blocksize\n");
674 	hdl->sio.eof = 1;
675 	return 0;
676 #undef NRETRIES
677 }
678 
679 static int
680 sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
681 {
682 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
683 	struct audio_info aui;
684 
685 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
686 		DPERROR("sio_sun_getpar: getinfo");
687 		hdl->sio.eof = 1;
688 		return 0;
689 	}
690 	if (hdl->sio.mode & SIO_PLAY) {
691 		par->rate = aui.play.sample_rate;
692 		if (!sio_sun_infotoenc(hdl, &aui.play, par))
693 			return 0;
694 	} else if (hdl->sio.mode & SIO_REC) {
695 		par->rate = aui.record.sample_rate;
696 		if (!sio_sun_infotoenc(hdl, &aui.record, par))
697 			return 0;
698 	} else
699 		return 0;
700 	par->pchan = (hdl->sio.mode & SIO_PLAY) ?
701 	    aui.play.channels : 0;
702 	par->rchan = (hdl->sio.mode & SIO_REC) ?
703 	    aui.record.channels : 0;
704 	par->round = (hdl->sio.mode & SIO_REC) ?
705 	    aui.record.block_size / (par->bps * par->rchan) :
706 	    aui.play.block_size / (par->bps * par->pchan);
707 	par->appbufsz = aui.hiwat * par->round;
708 	par->bufsz = par->appbufsz;
709 	par->xrun = SIO_IGNORE;
710 	return 1;
711 }
712 
713 static size_t
714 sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
715 {
716 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
717 	ssize_t n;
718 
719 	while ((n = read(hdl->fd, buf, len)) < 0) {
720 		if (errno == EINTR)
721 			continue;
722 		if (errno != EAGAIN) {
723 			DPERROR("sio_sun_read: read");
724 			hdl->sio.eof = 1;
725 		}
726 		return 0;
727 	}
728 	if (n == 0) {
729 		DPRINTF("sio_sun_read: eof\n");
730 		hdl->sio.eof = 1;
731 		return 0;
732 	}
733 	return n;
734 }
735 
736 static size_t
737 sio_sun_autostart(struct sio_sun_hdl *hdl)
738 {
739 	struct audio_info aui;
740 	struct pollfd pfd;
741 
742 	pfd.fd = hdl->fd;
743 	pfd.events = POLLOUT;
744 	while (poll(&pfd, 1, 0) < 0) {
745 		if (errno == EINTR)
746 			continue;
747 		DPERROR("sio_sun_autostart: poll");
748 		hdl->sio.eof = 1;
749 		return 0;
750 	}
751 	if (!(pfd.revents & POLLOUT)) {
752 		hdl->filling = 0;
753 		AUDIO_INITINFO(&aui);
754 		if (hdl->sio.mode & SIO_PLAY)
755 			aui.play.pause = 0;
756 		if (hdl->sio.mode & SIO_REC)
757 			aui.record.pause = 0;
758 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
759 			DPERROR("sio_sun_autostart: setinfo");
760 			hdl->sio.eof = 1;
761 			return 0;
762 		}
763 		_sio_onmove_cb(&hdl->sio, 0);
764 	}
765 	return 1;
766 }
767 
768 static size_t
769 sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
770 {
771 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
772 	const unsigned char *data = buf;
773 	ssize_t n, todo;
774 
775 	todo = len;
776 	while ((n = write(hdl->fd, data, todo)) < 0) {
777 		if (errno == EINTR)
778 			continue;
779 		if (errno != EAGAIN) {
780 			DPERROR("sio_sun_write: write");
781 			hdl->sio.eof = 1;
782 		}
783  		return 0;
784 	}
785 	if (hdl->filling) {
786 		if (!sio_sun_autostart(hdl))
787 			return 0;
788 	}
789 	return n;
790 }
791 
792 static int
793 sio_sun_nfds(struct sio_hdl *hdl)
794 {
795 	return 1;
796 }
797 
798 static int
799 sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
800 {
801 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
802 
803 	pfd->fd = hdl->fd;
804 	pfd->events = events;
805 	return 1;
806 }
807 
808 int
809 sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
810 {
811 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
812 	struct audio_pos ap;
813 	int dierr = 0, doerr = 0, offset, delta;
814 	int revents = pfd->revents;
815 
816 	if (!hdl->sio.started)
817 		return pfd->revents;
818 	if (ioctl(hdl->fd, AUDIO_GETPOS, &ap) < 0) {
819 		DPERROR("sio_sun_revents: GETPOS");
820 		hdl->sio.eof = 1;
821 		return POLLHUP;
822 	}
823 	if (hdl->sio.mode & SIO_PLAY) {
824 		delta = (ap.play_pos - hdl->obytes) / hdl->obpf;
825 		doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
826 		hdl->obytes = ap.play_pos;
827 		hdl->oerr = ap.play_xrun;
828 		hdl->odelta += delta;
829 		if (!(hdl->sio.mode & SIO_REC)) {
830 			hdl->idelta += delta;
831 			dierr = doerr;
832 		}
833 		if (doerr > 0)
834 			DPRINTFN(2, "play xrun %d\n", doerr);
835 	}
836 	if (hdl->sio.mode & SIO_REC) {
837 		delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf;
838 		dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf;
839 		hdl->ibytes = ap.rec_pos;
840 		hdl->ierr = ap.rec_xrun;
841 		hdl->idelta += delta;
842 		if (!(hdl->sio.mode & SIO_PLAY)) {
843 			hdl->odelta += delta;
844 			doerr = dierr;
845 		}
846 		if (dierr > 0)
847 			DPRINTFN(2, "rec xrun %d\n", dierr);
848 	}
849 
850 	/*
851 	 * GETPOS reports positions including xruns,
852 	 * so we have to substract to get the real position
853 	 */
854 	hdl->idelta -= dierr;
855 	hdl->odelta -= doerr;
856 
857 	offset = doerr - dierr;
858 	if (offset > 0) {
859 		hdl->sio.rdrop += offset * hdl->ibpf;
860 		hdl->idelta -= offset;
861 		DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr);
862 	} else if (offset < 0) {
863 		hdl->sio.wsil += -offset * hdl->obpf;
864 		hdl->odelta -= -offset;
865 		DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
866 	}
867 
868 	delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
869 	if (delta > 0) {
870 		_sio_onmove_cb(&hdl->sio, delta);
871 		hdl->idelta -= delta;
872 		hdl->odelta -= delta;
873 	}
874 
875 	if (hdl->filling)
876 		revents |= POLLOUT; /* XXX: is this necessary ? */
877 	return revents;
878 }
879