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