1*2c53affbSjmc /* $OpenBSD: sio_sun.c,v 1.30 2022/12/27 17:10:07 jmc Exp $ */
2b041ccb2Sratchov /*
3b041ccb2Sratchov * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4b041ccb2Sratchov *
5b041ccb2Sratchov * Permission to use, copy, modify, and distribute this software for any
6b041ccb2Sratchov * purpose with or without fee is hereby granted, provided that the above
7b041ccb2Sratchov * copyright notice and this permission notice appear in all copies.
8b041ccb2Sratchov *
9b041ccb2Sratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b041ccb2Sratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b041ccb2Sratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b041ccb2Sratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b041ccb2Sratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b041ccb2Sratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b041ccb2Sratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b041ccb2Sratchov */
17b041ccb2Sratchov
18b041ccb2Sratchov #include <sys/types.h>
19b041ccb2Sratchov #include <sys/ioctl.h>
20b041ccb2Sratchov #include <sys/audioio.h>
21b041ccb2Sratchov
22b041ccb2Sratchov #include <errno.h>
23b041ccb2Sratchov #include <fcntl.h>
24b041ccb2Sratchov #include <limits.h>
25b041ccb2Sratchov #include <poll.h>
26b041ccb2Sratchov #include <stdio.h>
27b041ccb2Sratchov #include <stdlib.h>
28b041ccb2Sratchov #include <string.h>
29b041ccb2Sratchov #include <unistd.h>
30b041ccb2Sratchov
3182bfc72bSratchov #include "debug.h"
32b041ccb2Sratchov #include "sio_priv.h"
33b041ccb2Sratchov
34130e1f1bSratchov #define DEVPATH_PREFIX "/dev/audio"
3528996f62Sratchov #define DEVPATH_MAX (1 + \
3628996f62Sratchov sizeof(DEVPATH_PREFIX) - 1 + \
3728996f62Sratchov sizeof(int) * 3)
3828996f62Sratchov
39b041ccb2Sratchov struct sio_sun_hdl {
40b041ccb2Sratchov struct sio_hdl sio;
41b041ccb2Sratchov int fd;
42b041ccb2Sratchov int filling;
437207b069Sratchov unsigned int ibpf, obpf; /* bytes per frame */
447207b069Sratchov unsigned int ibytes, obytes; /* bytes the hw transferred */
457207b069Sratchov unsigned int ierr, oerr; /* frames the hw dropped */
46b041ccb2Sratchov int idelta, odelta; /* position reported to client */
47b041ccb2Sratchov };
48b041ccb2Sratchov
49b041ccb2Sratchov static void sio_sun_close(struct sio_hdl *);
50b041ccb2Sratchov static int sio_sun_start(struct sio_hdl *);
51ec8a3410Sratchov static int sio_sun_flush(struct sio_hdl *);
52b041ccb2Sratchov static int sio_sun_setpar(struct sio_hdl *, struct sio_par *);
53b041ccb2Sratchov static int sio_sun_getpar(struct sio_hdl *, struct sio_par *);
54b041ccb2Sratchov static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *);
55b041ccb2Sratchov static size_t sio_sun_read(struct sio_hdl *, void *, size_t);
56b041ccb2Sratchov static size_t sio_sun_write(struct sio_hdl *, const void *, size_t);
57b041ccb2Sratchov static int sio_sun_nfds(struct sio_hdl *);
58b041ccb2Sratchov static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int);
59b041ccb2Sratchov static int sio_sun_revents(struct sio_hdl *, struct pollfd *);
60b041ccb2Sratchov
61b041ccb2Sratchov static struct sio_ops sio_sun_ops = {
62b041ccb2Sratchov sio_sun_close,
63b041ccb2Sratchov sio_sun_setpar,
64b041ccb2Sratchov sio_sun_getpar,
65b041ccb2Sratchov sio_sun_getcap,
66b041ccb2Sratchov sio_sun_write,
67b041ccb2Sratchov sio_sun_read,
68b041ccb2Sratchov sio_sun_start,
69ec8a3410Sratchov NULL,
70ec8a3410Sratchov sio_sun_flush,
71b041ccb2Sratchov sio_sun_nfds,
72b041ccb2Sratchov sio_sun_pollfd,
73b041ccb2Sratchov sio_sun_revents,
74b041ccb2Sratchov NULL, /* setvol */
75b041ccb2Sratchov NULL, /* getvol */
76b041ccb2Sratchov };
77b041ccb2Sratchov
78b041ccb2Sratchov static int
sio_sun_adjpar(struct sio_sun_hdl * hdl,struct audio_swpar * ap)797fa7c74cSratchov sio_sun_adjpar(struct sio_sun_hdl *hdl, struct audio_swpar *ap)
80b041ccb2Sratchov {
817fa7c74cSratchov if (hdl->sio.eof)
827fa7c74cSratchov return 0;
83df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_SETPAR, ap) == -1) {
847fa7c74cSratchov DPERROR("AUDIO_SETPAR");
857fa7c74cSratchov hdl->sio.eof = 1;
867fa7c74cSratchov return 0;
877fa7c74cSratchov }
88df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_GETPAR, ap) == -1) {
897fa7c74cSratchov DPERROR("AUDIO_GETPAR");
90b041ccb2Sratchov hdl->sio.eof = 1;
91b041ccb2Sratchov return 0;
92b041ccb2Sratchov }
93b041ccb2Sratchov return 1;
94b041ccb2Sratchov }
95b041ccb2Sratchov
96b041ccb2Sratchov /*
97b041ccb2Sratchov * try to set the device to the given parameters and check that the
98b041ccb2Sratchov * device can use them; return 1 on success, 0 on failure or error
99b041ccb2Sratchov */
100b041ccb2Sratchov static int
sio_sun_testpar(struct sio_sun_hdl * hdl,struct sio_enc * enc,unsigned int pchan,unsigned int rchan,unsigned int rate)1017fa7c74cSratchov sio_sun_testpar(struct sio_sun_hdl *hdl, struct sio_enc *enc,
1027207b069Sratchov unsigned int pchan, unsigned int rchan, unsigned int rate)
103b041ccb2Sratchov {
1047fa7c74cSratchov struct audio_swpar ap;
105b041ccb2Sratchov
1067fa7c74cSratchov AUDIO_INITPAR(&ap);
1077fa7c74cSratchov if (enc != NULL) {
1087fa7c74cSratchov ap.sig = enc->sig;
1097fa7c74cSratchov ap.bits = enc->bits;
1107fa7c74cSratchov ap.bps = enc->bps;
1117fa7c74cSratchov if (ap.bps > 1)
1127fa7c74cSratchov ap.le = enc->le;
1137fa7c74cSratchov if (ap.bps * 8 > ap.bits)
1147fa7c74cSratchov ap.msb = enc->msb;
115b041ccb2Sratchov }
116b041ccb2Sratchov if (rate)
1177fa7c74cSratchov ap.rate = rate;
118b041ccb2Sratchov if (pchan && (hdl->sio.mode & SIO_PLAY))
1197fa7c74cSratchov ap.pchan = pchan;
120b041ccb2Sratchov if (rchan && (hdl->sio.mode & SIO_REC))
1217fa7c74cSratchov ap.rchan = rchan;
1227fa7c74cSratchov if (!sio_sun_adjpar(hdl, &ap))
123b041ccb2Sratchov return 0;
1247fa7c74cSratchov if (pchan && ap.pchan != pchan)
125b041ccb2Sratchov return 0;
1267fa7c74cSratchov if (rchan && ap.rchan != rchan)
127b041ccb2Sratchov return 0;
1287fa7c74cSratchov if (rate && ap.rate != rate)
129b041ccb2Sratchov return 0;
1307fa7c74cSratchov if (enc) {
1317fa7c74cSratchov if (ap.sig != enc->sig)
132b041ccb2Sratchov return 0;
1337fa7c74cSratchov if (ap.bits != enc->bits)
134b041ccb2Sratchov return 0;
1357fa7c74cSratchov if (ap.bps != enc->bps)
1367fa7c74cSratchov return 0;
1377fa7c74cSratchov if (ap.bps > 1 && ap.le != enc->le)
1387fa7c74cSratchov return 0;
1397fa7c74cSratchov if (ap.bits < ap.bps * 8 && ap.msb != enc->msb)
140b041ccb2Sratchov return 0;
141b041ccb2Sratchov }
142b041ccb2Sratchov return 1;
143b041ccb2Sratchov }
144b041ccb2Sratchov
145b041ccb2Sratchov /*
146b041ccb2Sratchov * guess device capabilities
147b041ccb2Sratchov */
148b041ccb2Sratchov static int
sio_sun_getcap(struct sio_hdl * sh,struct sio_cap * cap)149b041ccb2Sratchov sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap)
150b041ccb2Sratchov {
1517207b069Sratchov static unsigned int chans[] = {
152b041ccb2Sratchov 1, 2, 4, 6, 8, 10, 12
153b041ccb2Sratchov };
1547207b069Sratchov static unsigned int rates[] = {
155b041ccb2Sratchov 8000, 11025, 12000, 16000, 22050, 24000,
156b041ccb2Sratchov 32000, 44100, 48000, 64000, 88200, 96000
157b041ccb2Sratchov };
1587fa7c74cSratchov static unsigned int encs[] = {
1597fa7c74cSratchov 8, 16, 24, 32
1607fa7c74cSratchov };
161b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
1627fa7c74cSratchov struct audio_swpar savepar, ap;
1637fa7c74cSratchov unsigned int nconf = 0;
1647207b069Sratchov unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
1657207b069Sratchov unsigned int i, j, conf;
166b041ccb2Sratchov
167df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_GETPAR, &savepar) == -1) {
1687fa7c74cSratchov DPERROR("AUDIO_GETPAR");
169b041ccb2Sratchov hdl->sio.eof = 1;
170b041ccb2Sratchov return 0;
171b041ccb2Sratchov }
1727fa7c74cSratchov
1737fa7c74cSratchov /*
1747fa7c74cSratchov * get a subset of supported encodings
1757fa7c74cSratchov */
1767fa7c74cSratchov for (i = 0; i < sizeof(encs) / sizeof(encs[0]); i++) {
1777fa7c74cSratchov AUDIO_INITPAR(&ap);
1787fa7c74cSratchov ap.bits = encs[i];
1797fa7c74cSratchov ap.sig = (ap.bits > 8) ? 1 : 0;
1807fa7c74cSratchov if (!sio_sun_adjpar(hdl, &ap))
1817fa7c74cSratchov return 0;
1827fa7c74cSratchov if (ap.bits == encs[i]) {
1837fa7c74cSratchov cap->enc[i].sig = ap.sig;
1847fa7c74cSratchov cap->enc[i].bits = ap.bits;
1857fa7c74cSratchov cap->enc[i].le = ap.le;
1867fa7c74cSratchov cap->enc[i].bps = ap.bps;
1877fa7c74cSratchov cap->enc[i].msb = ap.msb;
1887fa7c74cSratchov enc_map |= 1 << i;
189b041ccb2Sratchov }
190b041ccb2Sratchov }
191b041ccb2Sratchov
192b041ccb2Sratchov /*
193b041ccb2Sratchov * fill channels
194b041ccb2Sratchov *
195b041ccb2Sratchov * for now we're lucky: all kernel devices assume that the
196b041ccb2Sratchov * number of channels and the encoding are independent so we can
197b041ccb2Sratchov * use the current encoding and try various channels.
198b041ccb2Sratchov */
199b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) {
2007fa7c74cSratchov for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
2017fa7c74cSratchov AUDIO_INITPAR(&ap);
2027fa7c74cSratchov ap.pchan = chans[i];
2037fa7c74cSratchov if (!sio_sun_adjpar(hdl, &ap))
2047fa7c74cSratchov return 0;
2057fa7c74cSratchov if (ap.pchan == chans[i]) {
2067fa7c74cSratchov cap->pchan[i] = chans[i];
207b041ccb2Sratchov pchan_map |= (1 << i);
208b041ccb2Sratchov }
209b041ccb2Sratchov }
2107fa7c74cSratchov }
211b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) {
2127fa7c74cSratchov for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
2137fa7c74cSratchov AUDIO_INITPAR(&ap);
2147fa7c74cSratchov ap.pchan = chans[i];
2157fa7c74cSratchov if (!sio_sun_adjpar(hdl, &ap))
2167fa7c74cSratchov return 0;
2177fa7c74cSratchov if (ap.rchan == chans[i]) {
2187fa7c74cSratchov cap->rchan[i] = chans[i];
219b041ccb2Sratchov rchan_map |= (1 << i);
220b041ccb2Sratchov }
221b041ccb2Sratchov }
2227fa7c74cSratchov }
223b041ccb2Sratchov
224b041ccb2Sratchov /*
225b041ccb2Sratchov * fill rates
226b041ccb2Sratchov *
227b041ccb2Sratchov * rates are not independent from other parameters (eg. on
228b041ccb2Sratchov * uaudio devices), so certain rates may not be allowed with
229b041ccb2Sratchov * certain encodings. We have to check rates for all encodings
230b041ccb2Sratchov */
2317fa7c74cSratchov for (j = 0; j < sizeof(encs) / sizeof(encs[0]); j++) {
232b041ccb2Sratchov rate_map = 0;
2337fa7c74cSratchov if ((enc_map & (1 << j)) == 0)
2347fa7c74cSratchov continue;
2357fa7c74cSratchov for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
2367fa7c74cSratchov if (sio_sun_testpar(hdl,
2377fa7c74cSratchov &cap->enc[j], 0, 0, rates[i])) {
2387fa7c74cSratchov cap->rate[i] = rates[i];
239b041ccb2Sratchov rate_map |= (1 << i);
240b041ccb2Sratchov }
2417fa7c74cSratchov }
242b041ccb2Sratchov for (conf = 0; conf < nconf; conf++) {
243b041ccb2Sratchov if (cap->confs[conf].rate == rate_map) {
244b041ccb2Sratchov cap->confs[conf].enc |= (1 << j);
245b041ccb2Sratchov break;
246b041ccb2Sratchov }
247b041ccb2Sratchov }
248b041ccb2Sratchov if (conf == nconf) {
249b041ccb2Sratchov if (nconf == SIO_NCONF)
250b041ccb2Sratchov break;
251b041ccb2Sratchov cap->confs[nconf].enc = (1 << j);
252b041ccb2Sratchov cap->confs[nconf].pchan = pchan_map;
253b041ccb2Sratchov cap->confs[nconf].rchan = rchan_map;
254b041ccb2Sratchov cap->confs[nconf].rate = rate_map;
255b041ccb2Sratchov nconf++;
256b041ccb2Sratchov }
257b041ccb2Sratchov }
258b041ccb2Sratchov cap->nconf = nconf;
2597fa7c74cSratchov
260df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_SETPAR, &savepar) == -1) {
2617fa7c74cSratchov DPERROR("AUDIO_SETPAR");
2627fa7c74cSratchov hdl->sio.eof = 1;
263b041ccb2Sratchov return 0;
2647fa7c74cSratchov }
265b041ccb2Sratchov return 1;
266b041ccb2Sratchov }
267b041ccb2Sratchov
268bde5d162Sratchov int
sio_sun_getfd(const char * str,unsigned int mode,int nbio)269413b52fdSratchov sio_sun_getfd(const char *str, unsigned int mode, int nbio)
270b041ccb2Sratchov {
271163486f8Sratchov const char *p;
27228996f62Sratchov char path[DEVPATH_MAX];
273b44cb2caSratchov unsigned int devnum;
274413b52fdSratchov int fd, flags;
275b041ccb2Sratchov
276bde5d162Sratchov #ifdef DEBUG
277bde5d162Sratchov _sndio_debug_init();
278bde5d162Sratchov #endif
279163486f8Sratchov p = _sndio_parsetype(str, "rsnd");
280163486f8Sratchov if (p == NULL) {
281163486f8Sratchov DPRINTF("sio_sun_getfd: %s: \"rsnd\" expected\n", str);
282413b52fdSratchov return -1;
283b3956098Sratchov }
284163486f8Sratchov switch (*p) {
285163486f8Sratchov case '/':
286163486f8Sratchov p++;
287163486f8Sratchov break;
288163486f8Sratchov default:
289163486f8Sratchov DPRINTF("sio_sun_getfd: %s: '/' expected\n", str);
290163486f8Sratchov return -1;
291163486f8Sratchov }
292163486f8Sratchov p = _sndio_parsenum(p, &devnum, 255);
293163486f8Sratchov if (p == NULL || *p != '\0') {
294163486f8Sratchov DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str);
295413b52fdSratchov return -1;
296b44cb2caSratchov }
297b44cb2caSratchov snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
298b041ccb2Sratchov if (mode == (SIO_PLAY | SIO_REC))
299b041ccb2Sratchov flags = O_RDWR;
300b041ccb2Sratchov else
301b041ccb2Sratchov flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
302df69c215Sderaadt while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) == -1) {
303b041ccb2Sratchov if (errno == EINTR)
304b041ccb2Sratchov continue;
305b041ccb2Sratchov DPERROR(path);
306413b52fdSratchov return -1;
307b041ccb2Sratchov }
308413b52fdSratchov return fd;
309413b52fdSratchov }
310413b52fdSratchov
311bde5d162Sratchov struct sio_hdl *
sio_sun_fdopen(int fd,unsigned int mode,int nbio)312413b52fdSratchov sio_sun_fdopen(int fd, unsigned int mode, int nbio)
313413b52fdSratchov {
314413b52fdSratchov struct sio_sun_hdl *hdl;
315413b52fdSratchov
316bde5d162Sratchov #ifdef DEBUG
317bde5d162Sratchov _sndio_debug_init();
318bde5d162Sratchov #endif
319413b52fdSratchov hdl = malloc(sizeof(struct sio_sun_hdl));
320413b52fdSratchov if (hdl == NULL)
321413b52fdSratchov return NULL;
322413b52fdSratchov _sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
323b041ccb2Sratchov
324b041ccb2Sratchov /*
325b041ccb2Sratchov * pause the device
326b041ccb2Sratchov */
327df69c215Sderaadt if (ioctl(fd, AUDIO_STOP) == -1) {
3287fa7c74cSratchov DPERROR("AUDIO_STOP");
329b041ccb2Sratchov free(hdl);
330b041ccb2Sratchov return NULL;
331b041ccb2Sratchov }
3327fa7c74cSratchov hdl->fd = fd;
3337fa7c74cSratchov hdl->filling = 0;
3347fa7c74cSratchov return (struct sio_hdl *)hdl;
3357fa7c74cSratchov }
336b041ccb2Sratchov
337413b52fdSratchov struct sio_hdl *
_sio_sun_open(const char * str,unsigned int mode,int nbio)338413b52fdSratchov _sio_sun_open(const char *str, unsigned int mode, int nbio)
339413b52fdSratchov {
340413b52fdSratchov struct sio_hdl *hdl;
341413b52fdSratchov int fd;
342413b52fdSratchov
343413b52fdSratchov fd = sio_sun_getfd(str, mode, nbio);
344df69c215Sderaadt if (fd == -1)
345413b52fdSratchov return NULL;
346413b52fdSratchov hdl = sio_sun_fdopen(fd, mode, nbio);
347413b52fdSratchov if (hdl != NULL)
348413b52fdSratchov return hdl;
349df69c215Sderaadt while (close(fd) == -1 && errno == EINTR)
350413b52fdSratchov ; /* retry */
351413b52fdSratchov return NULL;
352413b52fdSratchov }
353413b52fdSratchov
354b041ccb2Sratchov static void
sio_sun_close(struct sio_hdl * sh)355b041ccb2Sratchov sio_sun_close(struct sio_hdl *sh)
356b041ccb2Sratchov {
357b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
358b041ccb2Sratchov
359df69c215Sderaadt while (close(hdl->fd) == -1 && errno == EINTR)
360b041ccb2Sratchov ; /* retry */
361b041ccb2Sratchov free(hdl);
362b041ccb2Sratchov }
363b041ccb2Sratchov
364b041ccb2Sratchov static int
sio_sun_start(struct sio_hdl * sh)365b041ccb2Sratchov sio_sun_start(struct sio_hdl *sh)
366b041ccb2Sratchov {
367b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
368b041ccb2Sratchov
369e6877796Sratchov hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
370e6877796Sratchov hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
371b041ccb2Sratchov hdl->ibytes = 0;
372b041ccb2Sratchov hdl->obytes = 0;
373b041ccb2Sratchov hdl->ierr = 0;
374b041ccb2Sratchov hdl->oerr = 0;
375b041ccb2Sratchov hdl->idelta = 0;
376b041ccb2Sratchov hdl->odelta = 0;
377b041ccb2Sratchov
378b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) {
379b041ccb2Sratchov /*
3807fa7c74cSratchov * keep the device paused and let sio_sun_pollfd() trigger the
381b041ccb2Sratchov * start later, to avoid buffer underruns
382b041ccb2Sratchov */
383b041ccb2Sratchov hdl->filling = 1;
384b041ccb2Sratchov } else {
385b041ccb2Sratchov /*
386b041ccb2Sratchov * no play buffers to fill, start now!
387b041ccb2Sratchov */
388df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_START) == -1) {
3897fa7c74cSratchov DPERROR("AUDIO_START");
390b041ccb2Sratchov hdl->sio.eof = 1;
391b041ccb2Sratchov return 0;
392b041ccb2Sratchov }
393d418f94bSratchov _sio_onmove_cb(&hdl->sio, 0);
394b041ccb2Sratchov }
395b041ccb2Sratchov return 1;
396b041ccb2Sratchov }
397b041ccb2Sratchov
398b041ccb2Sratchov static int
sio_sun_flush(struct sio_hdl * sh)399ec8a3410Sratchov sio_sun_flush(struct sio_hdl *sh)
400b041ccb2Sratchov {
401b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
402b041ccb2Sratchov
4037fa7c74cSratchov if (hdl->filling) {
4047fa7c74cSratchov hdl->filling = 0;
4057fa7c74cSratchov return 1;
406b041ccb2Sratchov }
407df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_STOP) == -1) {
4087fa7c74cSratchov DPERROR("AUDIO_STOP");
409b041ccb2Sratchov hdl->sio.eof = 1;
410b041ccb2Sratchov return 0;
411b041ccb2Sratchov }
412b041ccb2Sratchov return 1;
413b041ccb2Sratchov }
414b041ccb2Sratchov
415b041ccb2Sratchov static int
sio_sun_setpar(struct sio_hdl * sh,struct sio_par * par)416b041ccb2Sratchov sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
417b041ccb2Sratchov {
418b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
4197fa7c74cSratchov struct audio_swpar ap;
420b041ccb2Sratchov
4217fa7c74cSratchov AUDIO_INITPAR(&ap);
4227fa7c74cSratchov ap.sig = par->sig;
4237fa7c74cSratchov ap.le = par->le;
4247fa7c74cSratchov ap.bits = par->bits;
4257fa7c74cSratchov ap.bps = par->bps;
4267fa7c74cSratchov ap.msb = par->msb;
4277fa7c74cSratchov ap.rate = par->rate;
428b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY)
4297fa7c74cSratchov ap.pchan = par->pchan;
4307fa7c74cSratchov if (hdl->sio.mode & SIO_REC)
4317fa7c74cSratchov ap.rchan = par->rchan;
4327fa7c74cSratchov if (par->round != ~0U && par->appbufsz != ~0U) {
4337fa7c74cSratchov ap.round = par->round;
4347fa7c74cSratchov ap.nblks = par->appbufsz / par->round;
4357fa7c74cSratchov } else if (par->round != ~0U) {
4367fa7c74cSratchov ap.round = par->round;
4377fa7c74cSratchov ap.nblks = 2;
4387fa7c74cSratchov } else if (par->appbufsz != ~0U) {
4397fa7c74cSratchov ap.round = par->appbufsz / 2;
4407fa7c74cSratchov ap.nblks = 2;
4417fa7c74cSratchov }
442df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_SETPAR, &ap) == -1) {
4437fa7c74cSratchov DPERROR("AUDIO_SETPAR");
444b041ccb2Sratchov hdl->sio.eof = 1;
445b041ccb2Sratchov return 0;
446b041ccb2Sratchov }
447b041ccb2Sratchov return 1;
448b041ccb2Sratchov }
449b041ccb2Sratchov
450b041ccb2Sratchov static int
sio_sun_getpar(struct sio_hdl * sh,struct sio_par * par)451b041ccb2Sratchov sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
452b041ccb2Sratchov {
453b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
4547fa7c74cSratchov struct audio_swpar ap;
455b041ccb2Sratchov
456df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_GETPAR, &ap) == -1) {
4577fa7c74cSratchov DPERROR("AUDIO_GETPAR");
458b041ccb2Sratchov hdl->sio.eof = 1;
459b041ccb2Sratchov return 0;
460b041ccb2Sratchov }
4617fa7c74cSratchov par->sig = ap.sig;
4627fa7c74cSratchov par->le = ap.le;
4637fa7c74cSratchov par->bits = ap.bits;
4647fa7c74cSratchov par->bps = ap.bps;
4657fa7c74cSratchov par->msb = ap.msb;
4667fa7c74cSratchov par->rate = ap.rate;
4677fa7c74cSratchov par->pchan = ap.pchan;
4687fa7c74cSratchov par->rchan = ap.rchan;
4697fa7c74cSratchov par->round = ap.round;
4707fa7c74cSratchov par->appbufsz = par->bufsz = ap.nblks * ap.round;
47117645762Sratchov par->xrun = SIO_IGNORE;
472b041ccb2Sratchov return 1;
473b041ccb2Sratchov }
474b041ccb2Sratchov
475b041ccb2Sratchov static size_t
sio_sun_read(struct sio_hdl * sh,void * buf,size_t len)476b041ccb2Sratchov sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
477b041ccb2Sratchov {
478b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
479b041ccb2Sratchov ssize_t n;
480b041ccb2Sratchov
481df69c215Sderaadt while ((n = read(hdl->fd, buf, len)) == -1) {
482b041ccb2Sratchov if (errno == EINTR)
483b041ccb2Sratchov continue;
484b041ccb2Sratchov if (errno != EAGAIN) {
485b041ccb2Sratchov DPERROR("sio_sun_read: read");
486b041ccb2Sratchov hdl->sio.eof = 1;
487b041ccb2Sratchov }
488b041ccb2Sratchov return 0;
489b041ccb2Sratchov }
490b041ccb2Sratchov if (n == 0) {
491b041ccb2Sratchov DPRINTF("sio_sun_read: eof\n");
492b041ccb2Sratchov hdl->sio.eof = 1;
493b041ccb2Sratchov return 0;
494b041ccb2Sratchov }
495b041ccb2Sratchov return n;
496b041ccb2Sratchov }
497b041ccb2Sratchov
498b041ccb2Sratchov static size_t
sio_sun_write(struct sio_hdl * sh,const void * buf,size_t len)499b041ccb2Sratchov sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
500b041ccb2Sratchov {
501b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
502b041ccb2Sratchov const unsigned char *data = buf;
503b041ccb2Sratchov ssize_t n, todo;
504b041ccb2Sratchov
505b041ccb2Sratchov todo = len;
506df69c215Sderaadt while ((n = write(hdl->fd, data, todo)) == -1) {
507b041ccb2Sratchov if (errno == EINTR)
508b041ccb2Sratchov continue;
509b041ccb2Sratchov if (errno != EAGAIN) {
510b041ccb2Sratchov DPERROR("sio_sun_write: write");
511b041ccb2Sratchov hdl->sio.eof = 1;
512b041ccb2Sratchov }
513b041ccb2Sratchov return 0;
514b041ccb2Sratchov }
515b041ccb2Sratchov return n;
516b041ccb2Sratchov }
517b041ccb2Sratchov
518b041ccb2Sratchov static int
sio_sun_nfds(struct sio_hdl * hdl)519b041ccb2Sratchov sio_sun_nfds(struct sio_hdl *hdl)
520b041ccb2Sratchov {
521b041ccb2Sratchov return 1;
522b041ccb2Sratchov }
523b041ccb2Sratchov
524b041ccb2Sratchov static int
sio_sun_pollfd(struct sio_hdl * sh,struct pollfd * pfd,int events)525b041ccb2Sratchov sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
526b041ccb2Sratchov {
527b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
528b041ccb2Sratchov
529b041ccb2Sratchov pfd->fd = hdl->fd;
530b041ccb2Sratchov pfd->events = events;
5317fa7c74cSratchov if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz *
5327fa7c74cSratchov hdl->sio.par.pchan * hdl->sio.par.bps) {
5337fa7c74cSratchov hdl->filling = 0;
534df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_START) == -1) {
5357fa7c74cSratchov DPERROR("AUDIO_START");
5367fa7c74cSratchov hdl->sio.eof = 1;
5377fa7c74cSratchov return 0;
5387fa7c74cSratchov }
5397fa7c74cSratchov _sio_onmove_cb(&hdl->sio, 0);
5407fa7c74cSratchov }
541b041ccb2Sratchov return 1;
542b041ccb2Sratchov }
543b041ccb2Sratchov
544b041ccb2Sratchov int
sio_sun_revents(struct sio_hdl * sh,struct pollfd * pfd)545b041ccb2Sratchov sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
546b041ccb2Sratchov {
547b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
5489d7d964fSratchov struct audio_pos ap;
5499d7d964fSratchov int dierr = 0, doerr = 0, offset, delta;
550b041ccb2Sratchov int revents = pfd->revents;
551b041ccb2Sratchov
5527fa7c74cSratchov if ((pfd->revents & POLLHUP) ||
5537fa7c74cSratchov (pfd->revents & (POLLIN | POLLOUT)) == 0)
554b041ccb2Sratchov return pfd->revents;
555df69c215Sderaadt if (ioctl(hdl->fd, AUDIO_GETPOS, &ap) == -1) {
5569d7d964fSratchov DPERROR("sio_sun_revents: GETPOS");
557e7d2a37cSratchov hdl->sio.eof = 1;
558e7d2a37cSratchov return POLLHUP;
559e7d2a37cSratchov }
560b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) {
5619d7d964fSratchov delta = (ap.play_pos - hdl->obytes) / hdl->obpf;
5629d7d964fSratchov doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
5639d7d964fSratchov hdl->obytes = ap.play_pos;
5649d7d964fSratchov hdl->oerr = ap.play_xrun;
5659d7d964fSratchov hdl->odelta += delta;
5669d7d964fSratchov if (!(hdl->sio.mode & SIO_REC)) {
5679d7d964fSratchov hdl->idelta += delta;
568b041ccb2Sratchov dierr = doerr;
5699d7d964fSratchov }
570ea382012Sratchov if (doerr > 0)
571e791f9efSratchov DPRINTFN(2, "play xrun %d\n", doerr);
572b041ccb2Sratchov }
573b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) {
5749d7d964fSratchov delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf;
5759d7d964fSratchov dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf;
5769d7d964fSratchov hdl->ibytes = ap.rec_pos;
5779d7d964fSratchov hdl->ierr = ap.rec_xrun;
5789d7d964fSratchov hdl->idelta += delta;
5799d7d964fSratchov if (!(hdl->sio.mode & SIO_PLAY)) {
5809d7d964fSratchov hdl->odelta += delta;
581b041ccb2Sratchov doerr = dierr;
5829d7d964fSratchov }
583ea382012Sratchov if (dierr > 0)
584e791f9efSratchov DPRINTFN(2, "rec xrun %d\n", dierr);
585b041ccb2Sratchov }
586e6877796Sratchov
587e6877796Sratchov /*
5889d7d964fSratchov * GETPOS reports positions including xruns,
589*2c53affbSjmc * so we have to subtract to get the real position
590e6877796Sratchov */
591e6877796Sratchov hdl->idelta -= dierr;
592e6877796Sratchov hdl->odelta -= doerr;
593e6877796Sratchov
594ea382012Sratchov offset = doerr - dierr;
595ea382012Sratchov if (offset > 0) {
596ea382012Sratchov hdl->sio.rdrop += offset * hdl->ibpf;
597e6877796Sratchov hdl->idelta -= offset;
598e791f9efSratchov DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr);
599ea382012Sratchov } else if (offset < 0) {
600ea382012Sratchov hdl->sio.wsil += -offset * hdl->obpf;
6011664e8c8Sratchov hdl->odelta -= -offset;
602e791f9efSratchov DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
603ea382012Sratchov }
604b041ccb2Sratchov
605b041ccb2Sratchov delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
606b041ccb2Sratchov if (delta > 0) {
607d418f94bSratchov _sio_onmove_cb(&hdl->sio, delta);
608b041ccb2Sratchov hdl->idelta -= delta;
609b041ccb2Sratchov hdl->odelta -= delta;
610b041ccb2Sratchov }
611b041ccb2Sratchov return revents;
612b041ccb2Sratchov }
613