xref: /openbsd-src/lib/libsndio/sio_sun.c (revision 8a67bd4248b0acec91967249cf2b74aba1712039)
1 /*	$OpenBSD: sio_sun.c,v 1.3 2011/05/03 20:15:23 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 ibpf, obpf;		/* bytes per frame */
51 	unsigned ibytes, obytes;	/* bytes the hw transfered */
52 	unsigned ierr, oerr;		/* frames the hw dropped */
53 	int offset;			/* frames play is ahead of record */
54 	int idelta, odelta;		/* position reported to client */
55 	int mix_fd, mix_index;		/* /dev/mixerN stuff */
56 };
57 
58 static void sio_sun_close(struct sio_hdl *);
59 static int sio_sun_start(struct sio_hdl *);
60 static int sio_sun_stop(struct sio_hdl *);
61 static int sio_sun_setpar(struct sio_hdl *, struct sio_par *);
62 static int sio_sun_getpar(struct sio_hdl *, struct sio_par *);
63 static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *);
64 static size_t sio_sun_read(struct sio_hdl *, void *, size_t);
65 static size_t sio_sun_write(struct sio_hdl *, const void *, size_t);
66 static int sio_sun_nfds(struct sio_hdl *);
67 static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int);
68 static int sio_sun_revents(struct sio_hdl *, struct pollfd *);
69 
70 static struct sio_ops sio_sun_ops = {
71 	sio_sun_close,
72 	sio_sun_setpar,
73 	sio_sun_getpar,
74 	sio_sun_getcap,
75 	sio_sun_write,
76 	sio_sun_read,
77 	sio_sun_start,
78 	sio_sun_stop,
79 	sio_sun_nfds,
80 	sio_sun_pollfd,
81 	sio_sun_revents,
82 	NULL, /* setvol */
83 	NULL, /* getvol */
84 };
85 
86 /*
87  * convert sun encoding to sio_par encoding
88  */
89 static int
90 sio_sun_infotoenc(struct sio_sun_hdl *hdl, struct audio_prinfo *ai, struct sio_par *par)
91 {
92 	par->msb = ai->msb;
93 	par->bits = ai->precision;
94 	par->bps = ai->bps;
95 	switch (ai->encoding) {
96 	case AUDIO_ENCODING_SLINEAR_LE:
97 		par->le = 1;
98 		par->sig = 1;
99 		break;
100 	case AUDIO_ENCODING_SLINEAR_BE:
101 		par->le = 0;
102 		par->sig = 1;
103 		break;
104 	case AUDIO_ENCODING_ULINEAR_LE:
105 		par->le = 1;
106 		par->sig = 0;
107 		break;
108 	case AUDIO_ENCODING_ULINEAR_BE:
109 		par->le = 0;
110 		par->sig = 0;
111 		break;
112 	case AUDIO_ENCODING_SLINEAR:
113 		par->le = SIO_LE_NATIVE;
114 		par->sig = 1;
115 		break;
116 	case AUDIO_ENCODING_ULINEAR:
117 		par->le = SIO_LE_NATIVE;
118 		par->sig = 0;
119 		break;
120 	default:
121 		DPRINTF("sio_sun_infotoenc: unsupported encoding\n");
122 		hdl->sio.eof = 1;
123 		return 0;
124 	}
125 	return 1;
126 }
127 
128 /*
129  * convert sio_par encoding to sun encoding
130  */
131 static void
132 sio_sun_enctoinfo(struct sio_sun_hdl *hdl, unsigned *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 pchan, unsigned rchan, unsigned 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 chans[] = {
219 		1, 2, 4, 6, 8, 10, 12
220 	};
221 	static unsigned 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 nenc = 0, nconf = 0;
229 	unsigned enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
230 	unsigned 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));
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));
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));
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 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 	if (str == NULL)
347 		str = "";
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), "/dev/audio%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)) < 0) {
360 		if (errno == EINTR)
361 			continue;
362 		DPERROR(path);
363 		goto bad_free;
364 	}
365 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
366 		DPERROR("FD_CLOEXEC");
367 		goto bad_close;
368 	}
369 
370 	/*
371 	 * pause the device
372 	 */
373 	AUDIO_INITINFO(&aui);
374 	if (mode & SIO_PLAY)
375 		aui.play.pause = 1;
376 	if (mode & SIO_REC)
377 		aui.record.pause = 1;
378 	if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) {
379 		DPERROR("sio_open_sun: setinfo");
380 		goto bad_close;
381 	}
382 	/*
383 	 * If both play and record are requested then
384 	 * set full duplex mode.
385 	 */
386 	if (mode == (SIO_PLAY | SIO_REC)) {
387 		fullduplex = 1;
388 		if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) {
389 			DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path);
390 			goto bad_close;
391 		}
392 	}
393 	hdl->fd = fd;
394 
395 	/*
396 	 * Default parameters may not be compatible with libsndio (eg. mulaw
397 	 * encodings, different playback and recording parameters, etc...), so
398 	 * set parameters to a random value. If the requested parameters are
399 	 * not supported by the device, then sio_setpar() will pick supported
400 	 * ones.
401 	 */
402 	sio_initpar(&par);
403 	par.rate = 48000;
404 	par.le = SIO_LE_NATIVE;
405 	par.sig = 1;
406 	par.bits = 16;
407 	par.appbufsz = 1200;
408 	if (!sio_setpar(&hdl->sio, &par))
409 		goto bad_close;
410 	return (struct sio_hdl *)hdl;
411  bad_close:
412 	while (close(fd) < 0 && errno == EINTR)
413 		; /* retry */
414  bad_free:
415 	free(hdl);
416 	return NULL;
417 }
418 
419 static void
420 sio_sun_close(struct sio_hdl *sh)
421 {
422 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
423 
424 	while (close(hdl->fd) < 0 && errno == EINTR)
425 		; /* retry */
426 	free(hdl);
427 }
428 
429 static int
430 sio_sun_start(struct sio_hdl *sh)
431 {
432 	struct sio_par par;
433 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
434 	struct audio_info aui;
435 
436 	if (!sio_getpar(&hdl->sio, &par))
437 		return 0;
438 	hdl->obpf = par.pchan * par.bps;
439 	hdl->ibpf = par.rchan * par.bps;
440 	hdl->ibytes = 0;
441 	hdl->obytes = 0;
442 	hdl->ierr = 0;
443 	hdl->oerr = 0;
444 	hdl->offset = 0;
445 	hdl->idelta = 0;
446 	hdl->odelta = 0;
447 
448 	if (hdl->sio.mode & SIO_PLAY) {
449 		/*
450 		 * keep the device paused and let sio_sun_write() trigger the
451 		 * start later, to avoid buffer underruns
452 		 */
453 		hdl->filling = 1;
454 	} else {
455 		/*
456 		 * no play buffers to fill, start now!
457 		 */
458 		AUDIO_INITINFO(&aui);
459 		if (hdl->sio.mode & SIO_REC)
460 			aui.record.pause = 0;
461 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
462 			DPERROR("sio_sun_start: setinfo");
463 			hdl->sio.eof = 1;
464 			return 0;
465 		}
466 		hdl->filling = 0;
467 		sio_onmove_cb(&hdl->sio, 0);
468 	}
469 	return 1;
470 }
471 
472 static int
473 sio_sun_stop(struct sio_hdl *sh)
474 {
475 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
476 	struct audio_info aui;
477 	int mode;
478 
479 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
480 		DPERROR("sio_sun_stop: getinfo");
481 		hdl->sio.eof = 1;
482 		return 0;
483 	}
484 	mode = aui.mode;
485 
486 	/*
487 	 * there's no way to drain the device without blocking, so just
488 	 * stop it until the kernel driver get fixed
489 	 */
490 	AUDIO_INITINFO(&aui);
491 	aui.mode = 0;
492 	if (hdl->sio.mode & SIO_PLAY)
493 		aui.play.pause = 1;
494 	if (hdl->sio.mode & SIO_REC)
495 		aui.record.pause = 1;
496 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
497 		DPERROR("sio_sun_stop: setinfo1");
498 		hdl->sio.eof = 1;
499 		return 0;
500 	}
501 	AUDIO_INITINFO(&aui);
502 	aui.mode = mode;
503 	if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
504 		DPERROR("sio_sun_stop: setinfo2");
505 		hdl->sio.eof = 1;
506 		return 0;
507 	}
508 	return 1;
509 }
510 
511 static int
512 sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
513 {
514 #define NRETRIES 8
515 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
516 	struct audio_info aui;
517 	unsigned i, infr, ibpf, onfr, obpf;
518 	unsigned bufsz, round;
519 	unsigned rate, req_rate, prec, enc;
520 
521 	/*
522 	 * try to set parameters until the device accepts
523 	 * a common encoding and rate for play and record
524 	 */
525 	rate = par->rate;
526 	prec = par->bits;
527 	sio_sun_enctoinfo(hdl, &enc, par);
528 	for (i = 0;; i++) {
529 		if (i == NRETRIES) {
530 			DPRINTF("sio_sun_setpar: couldn't set parameters\n");
531 			hdl->sio.eof = 1;
532 			return 0;
533 		}
534 		AUDIO_INITINFO(&aui);
535 		if (hdl->sio.mode & SIO_PLAY) {
536 			aui.play.sample_rate = rate;
537 			aui.play.precision = prec;
538 			aui.play.encoding = enc;
539 			aui.play.channels = par->pchan;
540 		}
541 		if (hdl->sio.mode & SIO_REC) {
542 			aui.record.sample_rate = rate;
543 			aui.record.precision = prec;
544 			aui.record.encoding = enc;
545 			aui.record.channels = par->rchan;
546 		}
547 		DPRINTF("sio_sun_setpar: %i: trying pars = %u/%u/%u\n",
548 		    i, rate, prec, enc);
549 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) {
550 			DPERROR("sio_sun_setpar: setinfo(pars)");
551 			hdl->sio.eof = 1;
552 			return 0;
553 		}
554 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
555 			DPERROR("sio_sun_setpar: getinfo(pars)");
556 			hdl->sio.eof = 1;
557 			return 0;
558 		}
559 		enc = (hdl->sio.mode & SIO_REC) ?
560 		    aui.record.encoding : aui.play.encoding;
561 		switch (enc) {
562 		case AUDIO_ENCODING_SLINEAR_LE:
563 		case AUDIO_ENCODING_SLINEAR_BE:
564 		case AUDIO_ENCODING_ULINEAR_LE:
565 		case AUDIO_ENCODING_ULINEAR_BE:
566 		case AUDIO_ENCODING_SLINEAR:
567 		case AUDIO_ENCODING_ULINEAR:
568 			break;
569 		default:
570 			DPRINTF("sio_sun_setpar: couldn't set linear encoding\n");
571 			hdl->sio.eof = 1;
572 			return 0;
573 		}
574 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY))
575 			break;
576 		if (aui.play.sample_rate == aui.record.sample_rate &&
577 		    aui.play.precision == aui.record.precision &&
578 		    aui.play.encoding == aui.record.encoding)
579 			break;
580 		if (i < NRETRIES / 2) {
581 			rate = aui.play.sample_rate;
582 			prec = aui.play.precision;
583 			enc = aui.play.encoding;
584 		} else {
585 			rate = aui.record.sample_rate;
586 			prec = aui.record.precision;
587 			enc = aui.record.encoding;
588 		}
589 	}
590 
591 	/*
592 	 * If the rate that the hardware is using is different than
593 	 * the requested rate, scale buffer sizes so they will be the
594 	 * same time duration as what was requested.  This just gets
595 	 * the rates to use for scaling, that actual scaling is done
596 	 * later.
597 	 */
598 	rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate :
599 	    aui.play.sample_rate;
600 	req_rate = rate;
601 	if (par->rate && par->rate != ~0U)
602 		req_rate = par->rate;
603 
604 	/*
605 	 * if block size and buffer size are not both set then
606 	 * set the blocksize to half the buffer size
607 	 */
608 	bufsz = par->appbufsz;
609 	round = par->round;
610 	if (bufsz != ~0U) {
611 		bufsz = bufsz * rate / req_rate;
612 		if (round == ~0U)
613 			round = (bufsz + 1) / 2;
614 		else
615 			round = round * rate / req_rate;
616 	} else if (round != ~0U) {
617 		round = round * rate / req_rate;
618 		bufsz = round * 2;
619 	} else
620 		return 1;
621 
622 	/*
623 	 * get the play/record frame size in bytes
624 	 */
625 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
626 		DPERROR("sio_sun_setpar: GETINFO");
627 		hdl->sio.eof = 1;
628 		return 0;
629 	}
630 	ibpf = (hdl->sio.mode & SIO_REC) ?
631 	    aui.record.channels * aui.record.bps : 1;
632 	obpf = (hdl->sio.mode & SIO_PLAY) ?
633 	    aui.play.channels * aui.play.bps : 1;
634 
635 	DPRINTF("sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf);
636 
637 	/*
638 	 * try to set parameters until the device accepts
639 	 * a common block size for play and record
640 	 */
641 	for (i = 0; i < NRETRIES; i++) {
642 		AUDIO_INITINFO(&aui);
643 		aui.hiwat = (bufsz + round - 1) / round;
644 		aui.lowat = aui.hiwat;
645 		if (hdl->sio.mode & SIO_REC)
646 			aui.record.block_size = round * ibpf;
647 		if (hdl->sio.mode & SIO_PLAY)
648 			aui.play.block_size = round * obpf;
649 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
650 			DPERROR("sio_sun_setpar2: SETINFO");
651 			hdl->sio.eof = 1;
652 			return 0;
653 		}
654 		if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
655 			DPERROR("sio_sun_setpar2: GETINFO");
656 			hdl->sio.eof = 1;
657 			return 0;
658 		}
659 		infr = aui.record.block_size / ibpf;
660 		onfr = aui.play.block_size / obpf;
661 		DPRINTF("sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n",
662 		    i, round, infr, onfr);
663 
664 		/*
665 		 * if half-duplex or both block sizes match, we're done
666 		 */
667 		if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) {
668 			DPRINTF("sio_sun_setpar: blocksize ok\n");
669 			return 1;
670 		}
671 
672 		/*
673 		 * half of the retries, retry with the smaller value,
674 		 * then with the larger returned value
675 		 */
676 		if (i < NRETRIES / 2)
677 			round = infr < onfr ? infr : onfr;
678 		else
679 			round = infr < onfr ? onfr : infr;
680 	}
681 	DPRINTF("sio_sun_setpar: couldn't find a working blocksize\n");
682 	hdl->sio.eof = 1;
683 	return 0;
684 #undef NRETRIES
685 }
686 
687 static int
688 sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
689 {
690 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
691 	struct audio_info aui;
692 
693 	if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) {
694 		DPERROR("sio_sun_getpar: getinfo");
695 		hdl->sio.eof = 1;
696 		return 0;
697 	}
698 	if (hdl->sio.mode & SIO_PLAY) {
699 		par->rate = aui.play.sample_rate;
700 		if (!sio_sun_infotoenc(hdl, &aui.play, par))
701 			return 0;
702 	} else if (hdl->sio.mode & SIO_REC) {
703 		par->rate = aui.record.sample_rate;
704 		if (!sio_sun_infotoenc(hdl, &aui.record, par))
705 			return 0;
706 	} else
707 		return 0;
708 	par->pchan = (hdl->sio.mode & SIO_PLAY) ?
709 	    aui.play.channels : 0;
710 	par->rchan = (hdl->sio.mode & SIO_REC) ?
711 	    aui.record.channels : 0;
712 	par->round = (hdl->sio.mode & SIO_REC) ?
713 	    aui.record.block_size / (par->bps * par->rchan) :
714 	    aui.play.block_size / (par->bps * par->pchan);
715 	par->appbufsz = aui.hiwat * par->round;
716 	par->bufsz = par->appbufsz;
717 	return 1;
718 }
719 
720 /*
721  * drop recorded samples to compensate xruns
722  */
723 static int
724 sio_sun_rdrop(struct sio_sun_hdl *hdl)
725 {
726 #define DROP_NMAX 0x1000
727 	static char dropbuf[DROP_NMAX];
728 	ssize_t n, todo;
729 
730 	while (hdl->offset > 0) {
731 		todo = hdl->offset * hdl->ibpf;
732 		if (todo > DROP_NMAX)
733 			todo = DROP_NMAX - DROP_NMAX % hdl->ibpf;
734 		while ((n = read(hdl->fd, dropbuf, todo)) < 0) {
735 			if (errno == EINTR)
736 				continue;
737 			if (errno != EAGAIN) {
738 				DPERROR("sio_sun_rdrop: read");
739 				hdl->sio.eof = 1;
740 			}
741 			return 0;
742 		}
743 		if (n == 0) {
744 			DPRINTF("sio_sun_rdrop: eof\n");
745 			hdl->sio.eof = 1;
746 			return 0;
747 		}
748 		hdl->offset -= (int)n / (int)hdl->ibpf;
749 		DPRINTF("sio_sun_rdrop: dropped %ld/%ld bytes\n", n, todo);
750 	}
751 	return 1;
752 }
753 
754 static size_t
755 sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
756 {
757 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
758 	ssize_t n;
759 
760 	if (!sio_sun_rdrop(hdl))
761 		return 0;
762 	while ((n = read(hdl->fd, buf, len)) < 0) {
763 		if (errno == EINTR)
764 			continue;
765 		if (errno != EAGAIN) {
766 			DPERROR("sio_sun_read: read");
767 			hdl->sio.eof = 1;
768 		}
769 		return 0;
770 	}
771 	if (n == 0) {
772 		DPRINTF("sio_sun_read: eof\n");
773 		hdl->sio.eof = 1;
774 		return 0;
775 	}
776 	return n;
777 }
778 
779 static size_t
780 sio_sun_autostart(struct sio_sun_hdl *hdl)
781 {
782 	struct audio_info aui;
783 	struct pollfd pfd;
784 
785 	pfd.fd = hdl->fd;
786 	pfd.events = POLLOUT;
787 	while (poll(&pfd, 1, 0) < 0) {
788 		if (errno == EINTR)
789 			continue;
790 		DPERROR("sio_sun_autostart: poll");
791 		hdl->sio.eof = 1;
792 		return 0;
793 	}
794 	if (!(pfd.revents & POLLOUT)) {
795 		hdl->filling = 0;
796 		AUDIO_INITINFO(&aui);
797 		if (hdl->sio.mode & SIO_PLAY)
798 			aui.play.pause = 0;
799 		if (hdl->sio.mode & SIO_REC)
800 			aui.record.pause = 0;
801 		if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) {
802 			DPERROR("sio_sun_autostart: setinfo");
803 			hdl->sio.eof = 1;
804 			return 0;
805 		}
806 		sio_onmove_cb(&hdl->sio, 0);
807 	}
808 	return 1;
809 }
810 
811 /*
812  * insert silence to play to compensate xruns
813  */
814 static int
815 sio_sun_wsil(struct sio_sun_hdl *hdl)
816 {
817 #define ZERO_NMAX 0x1000
818 	static char zero[ZERO_NMAX];
819 	ssize_t n, todo;
820 
821 	while (hdl->offset < 0) {
822 		todo = (int)-hdl->offset * (int)hdl->obpf;
823 		if (todo > ZERO_NMAX)
824 			todo = ZERO_NMAX - ZERO_NMAX % hdl->obpf;
825 		while ((n = write(hdl->fd, zero, todo)) < 0) {
826 			if (errno == EINTR)
827 				continue;
828 			if (errno != EAGAIN) {
829 				DPERROR("sio_sun_wsil: write");
830 				hdl->sio.eof = 1;
831 				return 0;
832 			}
833 			return 0;
834 		}
835 		hdl->offset += (int)n / (int)hdl->obpf;
836 		DPRINTF("sio_sun_wsil: inserted %ld/%ld bytes\n", n, todo);
837 	}
838 	return 1;
839 }
840 
841 
842 static size_t
843 sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
844 {
845 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
846 	const unsigned char *data = buf;
847 	ssize_t n, todo;
848 
849 	if (!sio_sun_wsil(hdl))
850 		return 0;
851 	todo = len;
852 	while ((n = write(hdl->fd, data, todo)) < 0) {
853 		if (errno == EINTR)
854 			continue;
855 		if (errno != EAGAIN) {
856 			DPERROR("sio_sun_write: write");
857 			hdl->sio.eof = 1;
858 		}
859  		return 0;
860 	}
861 	if (hdl->filling) {
862 		if (!sio_sun_autostart(hdl))
863 			return 0;
864 	}
865 	return n;
866 }
867 
868 static int
869 sio_sun_nfds(struct sio_hdl *hdl)
870 {
871 	return 1;
872 }
873 
874 static int
875 sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
876 {
877 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
878 
879 	pfd->fd = hdl->fd;
880 	pfd->events = events;
881 	return 1;
882 }
883 
884 int
885 sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
886 {
887 	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
888 	struct audio_offset ao;
889 	int xrun, dmove, dierr = 0, doerr = 0, delta;
890 	int revents = pfd->revents;
891 
892 	if (!hdl->sio.started)
893 		return pfd->revents;
894 	if (hdl->sio.mode & SIO_PLAY) {
895 		if (ioctl(hdl->fd, AUDIO_PERROR, &xrun) < 0) {
896 			DPERROR("sio_sun_revents: PERROR");
897 			hdl->sio.eof = 1;
898 			return POLLHUP;
899 		}
900 		doerr = xrun - hdl->oerr;
901 		hdl->oerr = xrun;
902 		if (!(hdl->sio.mode & SIO_REC))
903 			dierr = doerr;
904 	}
905 	if (hdl->sio.mode & SIO_REC) {
906 		if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) {
907 			DPERROR("sio_sun_revents: RERROR");
908 			hdl->sio.eof = 1;
909 			return POLLHUP;
910 		}
911 		dierr = xrun - hdl->ierr;
912 		hdl->ierr = xrun;
913 		if (!(hdl->sio.mode & SIO_PLAY))
914 			doerr = dierr;
915 	}
916 	hdl->offset += doerr - dierr;
917 	dmove = dierr > doerr ? dierr : doerr;
918 	hdl->idelta -= dmove;
919 	hdl->odelta -= dmove;
920 
921 	if ((revents & POLLOUT) && (hdl->sio.mode & SIO_PLAY)) {
922 		if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) {
923 			DPERROR("sio_sun_revents: GETOOFFS");
924 			hdl->sio.eof = 1;
925 			return POLLHUP;
926 		}
927 		delta = (ao.samples - hdl->obytes) / hdl->obpf;
928 		hdl->obytes = ao.samples;
929 		hdl->odelta += delta;
930 		if (!(hdl->sio.mode & SIO_REC))
931 			hdl->idelta += delta;
932 	}
933 	if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) {
934 		if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) {
935 			DPERROR("sio_sun_revents: GETIOFFS");
936 			hdl->sio.eof = 1;
937 			return POLLHUP;
938 		}
939 		delta = (ao.samples - hdl->ibytes) / hdl->ibpf;
940 		hdl->ibytes = ao.samples;
941 		hdl->idelta += delta;
942 		if (!(hdl->sio.mode & SIO_PLAY))
943 			hdl->odelta += delta;
944 	}
945 	delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
946 	if (delta > 0) {
947 		sio_onmove_cb(&hdl->sio, delta);
948 		hdl->idelta -= delta;
949 		hdl->odelta -= delta;
950 	}
951 
952 	/*
953 	 * drop recorded samples or insert silence to play
954 	 * right now to adjust revents, and avoid busy loops
955 	 * programs
956 	 */
957 	if (hdl->filling)
958 		revents |= POLLOUT;
959 	if ((hdl->sio.mode & SIO_PLAY) && !sio_sun_wsil(hdl))
960 		revents &= ~POLLOUT;
961 	if ((hdl->sio.mode & SIO_REC) && !sio_sun_rdrop(hdl))
962 		revents &= ~POLLIN;
963 	return revents;
964 }
965