static int aucat_connect_un(struct aucat *hdl, unsigned int unit) { struct sockaddr_un ca; socklen_t len = sizeof(struct sockaddr_un); uid_t uid; int s; uid = geteuid(); snprintf(ca.sun_path, sizeof(ca.sun_path), "/tmp/aucat-%u/%s%u", uid, AUCAT_PATH, unit); ca.sun_family = AF_UNIX; s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) return 0; while (connect(s, (struct sockaddr *)&ca, len) < 0) { if (errno == EINTR) continue; DPERROR(ca.sun_path); /* try shared server */ snprintf(ca.sun_path, sizeof(ca.sun_path), "/tmp/aucat/%s%u", AUCAT_PATH, unit); while (connect(s, (struct sockaddr *)&ca, len) < 0) { if (errno == EINTR) continue; DPERROR(ca.sun_path); close(s); return 0; } break; } hdl->fd = s; DPRINTFN(2, "%s: connected\n", ca.sun_path); return 1; }
static size_t sio_sun_autostart(struct sio_sun_hdl *hdl) { struct audio_info aui; struct pollfd pfd; pfd.fd = hdl->fd; pfd.events = POLLOUT; while (poll(&pfd, 1, 0) < 0) { if (errno == EINTR) continue; DPERROR("sio_sun_autostart: poll"); hdl->sio.eof = 1; return 0; } if (!(pfd.revents & POLLOUT)) { hdl->filling = 0; AUDIO_INITINFO(&aui); if (hdl->sio.mode & SIO_PLAY) aui.play.pause = 0; if (hdl->sio.mode & SIO_REC) aui.record.pause = 0; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_sun_autostart: setinfo"); hdl->sio.eof = 1; return 0; } _sio_onmove_cb(&hdl->sio, 0); } return 1; }
/* * try to set the device to the given parameters and check that the * device can use them; return 1 on success, 0 on failure or error */ static int sio_sun_tryinfo(struct sio_sun_hdl *hdl, struct sio_enc *enc, unsigned int pchan, unsigned int rchan, unsigned int rate) { struct audio_info aui; struct audio_prinfo *pr; pr = (hdl->sio.mode & SIO_PLAY) ? &aui.play : &aui.record; AUDIO_INITINFO(&aui); if (enc) { if (enc->le && enc->sig) { pr->encoding = AUDIO_ENCODING_SLINEAR_LE; } else if (!enc->le && enc->sig) { pr->encoding = AUDIO_ENCODING_SLINEAR_BE; } else if (enc->le && !enc->sig) { pr->encoding = AUDIO_ENCODING_ULINEAR_LE; } else { pr->encoding = AUDIO_ENCODING_ULINEAR_BE; } pr->precision = enc->bits; } if (rate) pr->sample_rate = rate; if ((hdl->sio.mode & (SIO_PLAY | SIO_REC)) == (SIO_PLAY | SIO_REC)) aui.record = aui.play; if (pchan && (hdl->sio.mode & SIO_PLAY)) aui.play.channels = pchan; if (rchan && (hdl->sio.mode & SIO_REC)) aui.record.channels = rchan; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { if (errno == EINVAL) return 0; DPERROR("sio_sun_tryinfo: setinfo"); hdl->sio.eof = 1; return 0; } if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_tryinfo: getinfo"); hdl->sio.eof = 1; return 0; } if (pchan && aui.play.channels != pchan) return 0; if (rchan && aui.record.channels != rchan) return 0; if (rate) { if ((hdl->sio.mode & SIO_PLAY) && (aui.play.sample_rate != rate)) return 0; if ((hdl->sio.mode & SIO_REC) && (aui.record.sample_rate != rate)) return 0; } return 1; }
static int sio_psleep(struct sio_hdl *hdl, int event) { struct pollfd pfd[SIO_MAXNFDS]; int revents; int nfds; nfds = sio_nfds(hdl); if (nfds > SIO_MAXNFDS) { DPRINTF("sio_psleep: %d: too many descriptors\n", nfds); hdl->eof = 1; return 0; } for (;;) { nfds = sio_pollfd(hdl, pfd, event); while (poll(pfd, nfds, -1) < 0) { if (errno == EINTR) continue; DPERROR("sio_psleep: poll"); hdl->eof = 1; return 0; } revents = sio_revents(hdl, pfd); if (revents & POLLHUP) { DPRINTF("sio_psleep: hang-up\n"); return 0; } if (revents & event) break; } return 1; }
size_t _aucat_rdata(struct aucat *hdl, void *buf, size_t len, int *eof) { ssize_t n; if (hdl->rstate != RSTATE_DATA) { DPRINTF("_aucat_rdata: bad state\n"); abort(); } if (len > hdl->rtodo) len = hdl->rtodo; while ((n = read(hdl->fd, buf, len)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { *eof = 1; DPERROR("_aucat_rdata: read"); } return 0; } if (n == 0) { DPRINTF("_aucat_rdata: eof\n"); *eof = 1; return 0; } hdl->rtodo -= n; if (hdl->rtodo == 0) { hdl->rstate = RSTATE_MSG; hdl->rtodo = sizeof(struct amsg); } DPRINTFN(2, "_aucat_rdata: read: n = %zd\n", n); return n; }
struct fuse_vnode * alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t parent) { struct fuse_vnode *vn; if ((vn = malloc(sizeof(*vn))) == NULL) { DPERROR(__func__); return (NULL); } vn->ino = ino; vn->parent = parent; if (strlcpy(vn->path, path, sizeof(vn->path)) >= sizeof(vn->path)) { DPRINTF("%s: strlcpy name too long\n", __func__); free(vn); return (NULL); } if (ino == (ino_t)-1) { f->max_ino++; vn->ino = f->max_ino; } return (vn); }
static int sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_info aui; if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_getpar: getinfo"); hdl->sio.eof = 1; return 0; } if (hdl->sio.mode & SIO_PLAY) { par->rate = aui.play.sample_rate; if (!sio_sun_infotoenc(hdl, &aui.play, par)) return 0; } else if (hdl->sio.mode & SIO_REC) { par->rate = aui.record.sample_rate; if (!sio_sun_infotoenc(hdl, &aui.record, par)) return 0; } else return 0; par->pchan = (hdl->sio.mode & SIO_PLAY) ? aui.play.channels : 0; par->rchan = (hdl->sio.mode & SIO_REC) ? aui.record.channels : 0; par->round = (hdl->sio.mode & SIO_REC) ? aui.record.block_size / (par->bps * par->rchan) : aui.play.block_size / (par->bps * par->pchan); par->appbufsz = aui.hiwat * par->round; par->bufsz = par->appbufsz; return 1; }
static int aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit) { int s, error, opt; struct addrinfo *ailist, *ai, aihints; char serv[NI_MAXSERV]; snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT); memset(&aihints, 0, sizeof(struct addrinfo)); aihints.ai_socktype = SOCK_STREAM; aihints.ai_protocol = IPPROTO_TCP; error = getaddrinfo(host, serv, &aihints, &ailist); if (error) { DPRINTF("%s: %s\n", host, gai_strerror(error)); return 0; } s = -1; for (ai = ailist; ai != NULL; ai = ai->ai_next) { s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { DPERROR("socket"); continue; } restart: if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { if (errno == EINTR) goto restart; DPERROR("connect"); close(s); s = -1; continue; } break; } freeaddrinfo(ailist); if (s < 0) return 0; opt = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) < 0) { DPERROR("setsockopt"); close(s); return 0; } hdl->fd = s; return 1; }
void fuse_unmount(const char *dir, unused struct fuse_chan *ch) { if (ch->dead) return; if (unmount(dir, MNT_UPDATE) == -1) DPERROR(__func__); }
int _aucat_setfl(struct aucat *hdl, int nbio, int *eof) { if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) < 0) { DPERROR("_aucat_setfl: fcntl"); *eof = 1; return 0; } return 1; }
static int sio_sun_stop(struct sio_hdl *sh) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_info aui; int mode; if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_stop: getinfo"); hdl->sio.eof = 1; return 0; } mode = aui.mode; /* * there's no way to drain the device without blocking, so just * stop it until the kernel driver get fixed */ AUDIO_INITINFO(&aui); aui.mode = 0; if (hdl->sio.mode & SIO_PLAY) aui.play.pause = 1; if (hdl->sio.mode & SIO_REC) aui.record.pause = 1; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_sun_stop: setinfo1"); hdl->sio.eof = 1; return 0; } AUDIO_INITINFO(&aui); aui.mode = mode; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_sun_stop: setinfo2"); hdl->sio.eof = 1; return 0; } return 1; }
size_t _aucat_wdata(struct aucat *hdl, const void *buf, size_t len, unsigned int wbpf, int *eof) { ssize_t n; size_t datasize; switch (hdl->wstate) { case WSTATE_IDLE: datasize = len; if (datasize > AMSG_DATAMAX) datasize = AMSG_DATAMAX; datasize -= datasize % wbpf; if (datasize == 0) datasize = wbpf; hdl->wmsg.cmd = htonl(AMSG_DATA); hdl->wmsg.u.data.size = htonl(datasize); hdl->wtodo = sizeof(struct amsg); hdl->wstate = WSTATE_MSG; /* FALLTHROUGH */ case WSTATE_MSG: if (!_aucat_wmsg(hdl, eof)) return 0; } if (len > hdl->wtodo) len = hdl->wtodo; if (len == 0) { DPRINTF("_aucat_wdata: len == 0\n"); abort(); } while ((n = write(hdl->fd, buf, len)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { *eof = 1; DPERROR("_aucat_wdata: write"); } return 0; } DPRINTFN(2, "_aucat_wdata: write: n = %zd\n", n); hdl->wtodo -= n; if (hdl->wtodo == 0) { hdl->wstate = WSTATE_IDLE; hdl->wtodo = 0xdeadbeef; } return n; }
struct fuse_vnode * alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t parent) { struct fuse_vnode *vn; if ((vn = malloc(sizeof(*vn))) == NULL) { DPERROR("alloc_vn:"); return (NULL); } vn->ino = ino; vn->parent = parent; strncpy(vn->path, path, NAME_MAX); vn->path[NAME_MAX - 1] = '\0'; if (ino == (ino_t)-1) { f->max_ino++; vn->ino = f->max_ino; } return (vn); }
static size_t sio_sun_read(struct sio_hdl *sh, void *buf, size_t len) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; ssize_t n; while ((n = read(hdl->fd, buf, len)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { DPERROR("sio_sun_read: read"); hdl->sio.eof = 1; } return 0; } if (n == 0) { DPRINTF("sio_sun_read: eof\n"); hdl->sio.eof = 1; return 0; } return n; }
/* * read a message, return 0 if not completed */ int _aucat_rmsg(struct aucat *hdl, int *eof) { ssize_t n; unsigned char *data; if (hdl->rstate != RSTATE_MSG) { DPRINTF("_aucat_rmsg: bad state\n"); abort(); } while (hdl->rtodo > 0) { data = (unsigned char *)&hdl->rmsg; data += sizeof(struct amsg) - hdl->rtodo; while ((n = read(hdl->fd, data, hdl->rtodo)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { *eof = 1; DPERROR("_aucat_rmsg: read"); } return 0; } if (n == 0) { DPRINTF("_aucat_rmsg: eof\n"); *eof = 1; return 0; } hdl->rtodo -= n; } if (ntohl(hdl->rmsg.cmd) == AMSG_DATA) { hdl->rtodo = ntohl(hdl->rmsg.u.data.size); hdl->rstate = RSTATE_DATA; } else { hdl->rtodo = sizeof(struct amsg); hdl->rstate = RSTATE_MSG; } return 1; }
static int sio_sun_start(struct sio_hdl *sh) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_info aui; hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps; hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps; hdl->ibytes = 0; hdl->obytes = 0; hdl->ierr = 0; hdl->oerr = 0; hdl->idelta = 0; hdl->odelta = 0; if (hdl->sio.mode & SIO_PLAY) { /* * keep the device paused and let sio_sun_write() trigger the * start later, to avoid buffer underruns */ hdl->filling = 1; } else { /* * no play buffers to fill, start now! */ AUDIO_INITINFO(&aui); if (hdl->sio.mode & SIO_REC) aui.record.pause = 0; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_sun_start: setinfo"); hdl->sio.eof = 1; return 0; } hdl->filling = 0; _sio_onmove_cb(&hdl->sio, 0); } return 1; }
static size_t sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; const unsigned char *data = buf; ssize_t n, todo; todo = len; while ((n = write(hdl->fd, data, todo)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { DPERROR("sio_sun_write: write"); hdl->sio.eof = 1; } return 0; } if (hdl->filling) { if (!sio_sun_autostart(hdl)) return 0; } return n; }
/* * write a message, return 0 if not completed */ int _aucat_wmsg(struct aucat *hdl, int *eof) { ssize_t n; unsigned char *data; if (hdl->wstate == WSTATE_IDLE) hdl->wstate = WSTATE_MSG; hdl->wtodo = sizeof(struct amsg); if (hdl->wstate != WSTATE_MSG) { DPRINTF("_aucat_wmsg: bad state\n"); abort(); } while (hdl->wtodo > 0) { data = (unsigned char *)&hdl->wmsg; data += sizeof(struct amsg) - hdl->wtodo; while ((n = write(hdl->fd, data, hdl->wtodo)) < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { *eof = 1; DPERROR("_aucat_wmsg: write"); } return 0; } hdl->wtodo -= n; } if (ntohl(hdl->wmsg.cmd) == AMSG_DATA) { hdl->wtodo = ntohl(hdl->wmsg.u.data.size); hdl->wstate = WSTATE_DATA; } else { hdl->wtodo = 0xdeadbeef; hdl->wstate = WSTATE_IDLE; } return 1; }
int _aucat_open(struct aucat *hdl, const char *str, unsigned int mode, unsigned int type) { extern char *__progname; int eof; char host[NI_MAXHOST], opt[AMSG_OPTMAX]; const char *p = str; unsigned int unit, devnum; if (*p == '@') { p = parsestr(++p, host, NI_MAXHOST); if (p == NULL) return 0; } else *host = '\0'; if (*p == ',') { p = parsedev(++p, &unit); if (p == NULL) return 0; } else unit = 0; if (*p != '/' && *p != ':') { DPRINTF("%s: '/' expected\n", str); return 0; } p = parsedev(++p, &devnum); if (p == NULL) return 0; if (*p == '.') { p = parsestr(++p, opt, AMSG_OPTMAX); if (p == NULL) return 0; } else strlcpy(opt, "default", AMSG_OPTMAX); if (*p != '\0') { DPRINTF("%s: junk at end of dev name\n", p); return 0; } devnum += type * 16; /* XXX */ DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n", host, unit, devnum, opt); if (host[0] != '\0') { if (!aucat_connect_tcp(hdl, host, unit)) return 0; } else { if (!aucat_connect_un(hdl, unit)) return 0; } if (fcntl(hdl->fd, F_SETFD, FD_CLOEXEC) < 0) { DPERROR("FD_CLOEXEC"); goto bad_connect; } hdl->rstate = RSTATE_MSG; hdl->rtodo = sizeof(struct amsg); hdl->wstate = WSTATE_IDLE; hdl->wtodo = 0xdeadbeef; hdl->maxwrite = 0; /* * say hello to server */ AMSG_INIT(&hdl->wmsg); hdl->wmsg.cmd = htonl(AMSG_AUTH); if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie)) goto bad_connect; hdl->wtodo = sizeof(struct amsg); if (!_aucat_wmsg(hdl, &eof)) goto bad_connect; AMSG_INIT(&hdl->wmsg); hdl->wmsg.cmd = htonl(AMSG_HELLO); hdl->wmsg.u.hello.version = AMSG_VERSION; hdl->wmsg.u.hello.mode = htons(mode); hdl->wmsg.u.hello.devnum = devnum; strlcpy(hdl->wmsg.u.hello.who, __progname, sizeof(hdl->wmsg.u.hello.who)); strlcpy(hdl->wmsg.u.hello.opt, opt, sizeof(hdl->wmsg.u.hello.opt)); hdl->wtodo = sizeof(struct amsg); if (!_aucat_wmsg(hdl, &eof)) goto bad_connect; hdl->rtodo = sizeof(struct amsg); if (!_aucat_rmsg(hdl, &eof)) { DPRINTF("aucat_init: mode refused\n"); goto bad_connect; } if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) { DPRINTF("aucat_init: protocol err\n"); goto bad_connect; } return 1; bad_connect: while (close(hdl->fd) < 0 && errno == EINTR) ; /* retry */ return 0; }
int fuse_loop(struct fuse *fuse) { struct fusebuf fbuf; struct fuse_context ctx; struct fb_ioctl_xch ioexch; struct kevent ev; ssize_t n; int ret; fuse->fc->kq = kqueue(); if (fuse->fc->kq == -1) return (-1); EV_SET(&fuse->fc->event, fuse->fc->fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); while (!fuse->fc->dead) { ret = kevent(fuse->fc->kq, &fuse->fc->event, 1, &ev, 1, NULL); if (ret == -1) DPERROR(__func__); else if (ret > 0) { n = read(fuse->fc->fd, &fbuf, sizeof(fbuf)); if (n != sizeof(fbuf)) { fprintf(stderr, "%s: bad fusebuf read\n", __func__); return (-1); } /* check if there is data something present */ if (fbuf.fb_len) { fbuf.fb_dat = malloc(fbuf.fb_len); if (fbuf.fb_dat == NULL) return (-1); ioexch.fbxch_uuid = fbuf.fb_uuid; ioexch.fbxch_len = fbuf.fb_len; ioexch.fbxch_data = fbuf.fb_dat; if (ioctl(fuse->fc->fd, FIOCGETFBDAT, &ioexch)) { free(fbuf.fb_dat); return (-1); } } ctx.fuse = fuse; ctx.uid = fuse->conf.uid; ctx.gid = fuse->conf.gid; ctx.pid = fuse->conf.pid; ctx.umask = fuse->conf.umask; ctx.private_data = fuse->private_data; ictx = &ctx; ret = ifuse_exec_opcode(fuse, &fbuf); if (ret) { ictx = NULL; return (-1); } n = write(fuse->fc->fd, &fbuf, sizeof(fbuf)); if (fbuf.fb_len) { if (fbuf.fb_dat == NULL) { fprintf(stderr, "%s: fb_dat is Null\n", __func__); return (-1); } ioexch.fbxch_uuid = fbuf.fb_uuid; ioexch.fbxch_len = fbuf.fb_len; ioexch.fbxch_data = fbuf.fb_dat; if (ioctl(fuse->fc->fd, FIOCSETFBDAT, &ioexch)) { free(fbuf.fb_dat); return (-1); } free(fbuf.fb_dat); } ictx = NULL; if (n != FUSEBUFSIZE) { errno = EINVAL; return (-1); } } } return (0); }
/* * guess device capabilities */ static int sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap) { #define NCHANS (sizeof(chans) / sizeof(chans[0])) #define NRATES (sizeof(rates) / sizeof(rates[0])) static unsigned int chans[] = { 1, 2, 4, 6, 8, 10, 12 }; static unsigned int rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct sio_par savepar; struct audio_encoding ae; unsigned int nenc = 0, nconf = 0; unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map; unsigned int i, j, conf; if (!sio_sun_getpar(&hdl->sio, &savepar)) return 0; /* * fill encoding list */ for (ae.index = 0; nenc < SIO_NENC; ae.index++) { if (ioctl(hdl->fd, AUDIO_GETENC, &ae) < 0) { if (errno == EINVAL) break; DPERROR("sio_sun_getcap: getenc"); hdl->sio.eof = 1; return 0; } if (ae.flags & AUDIO_ENCODINGFLAG_EMULATED) continue; if (ae.encoding == AUDIO_ENCODING_SLINEAR_LE) { cap->enc[nenc].le = 1; cap->enc[nenc].sig = 1; } else if (ae.encoding == AUDIO_ENCODING_SLINEAR_BE) { cap->enc[nenc].le = 0; cap->enc[nenc].sig = 1; } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_LE) { cap->enc[nenc].le = 1; cap->enc[nenc].sig = 0; } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_BE) { cap->enc[nenc].le = 0; cap->enc[nenc].sig = 0; } else if (ae.encoding == AUDIO_ENCODING_SLINEAR) { cap->enc[nenc].le = SIO_LE_NATIVE; cap->enc[nenc].sig = 1; } else if (ae.encoding == AUDIO_ENCODING_ULINEAR) { cap->enc[nenc].le = SIO_LE_NATIVE; cap->enc[nenc].sig = 0; } else { /* unsipported encoding */ continue; } cap->enc[nenc].bits = ae.precision; cap->enc[nenc].bps = ae.bps; cap->enc[nenc].msb = ae.msb; enc_map |= (1 << nenc); nenc++; } /* * fill channels * * for now we're lucky: all kernel devices assume that the * number of channels and the encoding are independent so we can * use the current encoding and try various channels. */ if (hdl->sio.mode & SIO_PLAY) { memcpy(&cap->pchan, chans, NCHANS * sizeof(unsigned int)); for (i = 0; i < NCHANS; i++) { if (sio_sun_tryinfo(hdl, NULL, chans[i], 0, 0)) pchan_map |= (1 << i); } } if (hdl->sio.mode & SIO_REC) { memcpy(&cap->rchan, chans, NCHANS * sizeof(unsigned int)); for (i = 0; i < NCHANS; i++) { if (sio_sun_tryinfo(hdl, NULL, 0, chans[i], 0)) rchan_map |= (1 << i); } } /* * fill rates * * rates are not independent from other parameters (eg. on * uaudio devices), so certain rates may not be allowed with * certain encodings. We have to check rates for all encodings */ memcpy(&cap->rate, rates, NRATES * sizeof(unsigned int)); for (j = 0; j < nenc; j++) { rate_map = 0; for (i = 0; i < NRATES; i++) { if (sio_sun_tryinfo(hdl, &cap->enc[j], 0, 0, rates[i])) rate_map |= (1 << i); } for (conf = 0; conf < nconf; conf++) { if (cap->confs[conf].rate == rate_map) { cap->confs[conf].enc |= (1 << j); break; } } if (conf == nconf) { if (nconf == SIO_NCONF) break; cap->confs[nconf].enc = (1 << j); cap->confs[nconf].pchan = pchan_map; cap->confs[nconf].rchan = rchan_map; cap->confs[nconf].rate = rate_map; nconf++; } } cap->nconf = nconf; if (!sio_sun_setpar(&hdl->sio, &savepar)) return 0; return 1; #undef NCHANS #undef NRATES }
struct sio_hdl * _sio_sun_open(const char *str, unsigned int mode, int nbio) { int fd, flags, fullduplex; struct audio_info aui; struct sio_sun_hdl *hdl; struct sio_par par; char path[PATH_MAX]; switch (*str) { case '/': case ':': /* XXX: for backward compat */ str++; break; default: DPRINTF("_sio_sun_open: %s: '/<devnum>' expected\n", str); return NULL; } hdl = malloc(sizeof(struct sio_sun_hdl)); if (hdl == NULL) return NULL; _sio_create(&hdl->sio, &sio_sun_ops, mode, nbio); snprintf(path, sizeof(path), "/dev/audio%s", str); if (mode == (SIO_PLAY | SIO_REC)) flags = O_RDWR; else flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY; while ((fd = open(path, flags | O_NONBLOCK)) < 0) { if (errno == EINTR) continue; DPERROR(path); goto bad_free; } if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { DPERROR("FD_CLOEXEC"); goto bad_close; } /* * pause the device */ AUDIO_INITINFO(&aui); if (mode & SIO_PLAY) aui.play.pause = 1; if (mode & SIO_REC) aui.record.pause = 1; if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_open_sun: setinfo"); goto bad_close; } /* * If both play and record are requested then * set full duplex mode. */ if (mode == (SIO_PLAY | SIO_REC)) { fullduplex = 1; if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) { DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path); goto bad_close; } } hdl->fd = fd; /* * Default parameters may not be compatible with libsndio (eg. mulaw * encodings, different playback and recording parameters, etc...), so * set parameters to a random value. If the requested parameters are * not supported by the device, then sio_setpar() will pick supported * ones. */ sio_initpar(&par); par.rate = 48000; par.le = SIO_LE_NATIVE; par.sig = 1; par.bits = 16; par.appbufsz = 1200; if (!sio_setpar(&hdl->sio, &par)) goto bad_close; return (struct sio_hdl *)hdl; bad_close: while (close(fd) < 0 && errno == EINTR) ; /* retry */ bad_free: free(hdl); return NULL; }
int sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_offset ao; int xrun, dierr = 0, doerr = 0, offset, delta; int revents = pfd->revents; if (!hdl->sio.started) return pfd->revents; if (hdl->sio.mode & SIO_PLAY) { if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) { DPERROR("sio_sun_revents: GETOOFFS"); hdl->sio.eof = 1; return POLLHUP; } delta = (ao.samples - hdl->obytes) / hdl->obpf; hdl->obytes = ao.samples; hdl->odelta += delta; if (!(hdl->sio.mode & SIO_REC)) hdl->idelta += delta; } if (hdl->sio.mode & SIO_REC) { if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) { DPERROR("sio_sun_revents: GETIOFFS"); hdl->sio.eof = 1; return POLLHUP; } delta = (ao.samples - hdl->ibytes) / hdl->ibpf; hdl->ibytes = ao.samples; hdl->idelta += delta; if (!(hdl->sio.mode & SIO_PLAY)) hdl->odelta += delta; } if (hdl->sio.mode & SIO_PLAY) { if (ioctl(hdl->fd, AUDIO_PERROR, &xrun) < 0) { DPERROR("sio_sun_revents: PERROR"); hdl->sio.eof = 1; return POLLHUP; } doerr = xrun - hdl->oerr; hdl->oerr = xrun; if (!(hdl->sio.mode & SIO_REC)) dierr = doerr; if (doerr > 0) DPRINTFN(2, "play xrun %d\n", doerr); } if (hdl->sio.mode & SIO_REC) { if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) { DPERROR("sio_sun_revents: RERROR"); hdl->sio.eof = 1; return POLLHUP; } dierr = xrun - hdl->ierr; hdl->ierr = xrun; if (!(hdl->sio.mode & SIO_PLAY)) doerr = dierr; if (dierr > 0) DPRINTFN(2, "rec xrun %d\n", dierr); } /* * GET{I,O}OFFS report positions including xruns, * so we have to substract to get the real position */ hdl->idelta -= dierr; hdl->odelta -= doerr; offset = doerr - dierr; if (offset > 0) { hdl->sio.rdrop += offset * hdl->ibpf; hdl->idelta -= offset; DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr); } else if (offset < 0) { hdl->sio.wsil += -offset * hdl->obpf; hdl->odelta -= offset; DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr); } delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta; if (delta > 0) { _sio_onmove_cb(&hdl->sio, delta); hdl->idelta -= delta; hdl->odelta -= delta; } if (hdl->filling) revents |= POLLOUT; /* XXX: is this necessary ? */ return revents; }
static int aucat_mkcookie(unsigned char *cookie) { struct stat sb; char buf[PATH_MAX], tmp[PATH_MAX], *path; ssize_t len; int fd; /* * try to load the cookie */ path = issetugid() ? NULL : getenv("AUCAT_COOKIE"); if (path == NULL) { path = issetugid() ? NULL : getenv("HOME"); if (path == NULL) goto bad_gen; snprintf(buf, PATH_MAX, "%s/.aucat_cookie", path); path = buf; } fd = open(path, O_RDONLY); if (fd < 0) { if (errno != ENOENT) DPERROR(path); goto bad_gen; } if (fstat(fd, &sb) < 0) { DPERROR(path); goto bad_close; } if (sb.st_mode & 0077) { DPRINTF("%s has wrong permissions\n", path); goto bad_close; } len = read(fd, cookie, AMSG_COOKIELEN); if (len < 0) { DPERROR(path); goto bad_close; } if (len != AMSG_COOKIELEN) { DPRINTF("%s: short read\n", path); goto bad_close; } close(fd); return 1; bad_close: close(fd); bad_gen: /* * generate a new cookie */ arc4random_buf(cookie, AMSG_COOKIELEN); /* * try to save the cookie */ if (path == NULL) return 1; if (strlcpy(tmp, path, PATH_MAX) >= PATH_MAX || strlcat(tmp, ".XXXXXXXX", PATH_MAX) >= PATH_MAX) { DPRINTF("%s: too long\n", path); return 1; } fd = mkstemp(tmp); if (fd < 0) { DPERROR(tmp); return 1; } if (write(fd, cookie, AMSG_COOKIELEN) < 0) { DPERROR(tmp); unlink(tmp); close(fd); return 1; } close(fd); if (rename(tmp, path) < 0) { DPERROR(tmp); unlink(tmp); } return 1; }
static int sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par) { #define NRETRIES 8 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_info aui; unsigned int i, infr, ibpf, onfr, obpf; unsigned int bufsz, round; unsigned int rate, req_rate, prec, enc; /* * try to set parameters until the device accepts * a common encoding and rate for play and record */ rate = par->rate; prec = par->bits; sio_sun_enctoinfo(hdl, &enc, par); for (i = 0;; i++) { if (i == NRETRIES) { DPRINTF("sio_sun_setpar: couldn't set parameters\n"); hdl->sio.eof = 1; return 0; } AUDIO_INITINFO(&aui); if (hdl->sio.mode & SIO_PLAY) { aui.play.sample_rate = rate; aui.play.precision = prec; aui.play.encoding = enc; aui.play.channels = par->pchan; } if (hdl->sio.mode & SIO_REC) { aui.record.sample_rate = rate; aui.record.precision = prec; aui.record.encoding = enc; aui.record.channels = par->rchan; } DPRINTFN(2, "sio_sun_setpar: %i: trying pars = %u/%u/%u\n", i, rate, prec, enc); if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) { DPERROR("sio_sun_setpar: setinfo(pars)"); hdl->sio.eof = 1; return 0; } if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_setpar: getinfo(pars)"); hdl->sio.eof = 1; return 0; } enc = (hdl->sio.mode & SIO_REC) ? aui.record.encoding : aui.play.encoding; switch (enc) { case AUDIO_ENCODING_SLINEAR_LE: case AUDIO_ENCODING_SLINEAR_BE: case AUDIO_ENCODING_ULINEAR_LE: case AUDIO_ENCODING_ULINEAR_BE: case AUDIO_ENCODING_SLINEAR: case AUDIO_ENCODING_ULINEAR: break; default: DPRINTF("sio_sun_setpar: couldn't set linear encoding\n"); hdl->sio.eof = 1; return 0; } if (hdl->sio.mode != (SIO_REC | SIO_PLAY)) break; if (aui.play.sample_rate == aui.record.sample_rate && aui.play.precision == aui.record.precision && aui.play.encoding == aui.record.encoding) break; if (i < NRETRIES / 2) { rate = aui.play.sample_rate; prec = aui.play.precision; enc = aui.play.encoding; } else { rate = aui.record.sample_rate; prec = aui.record.precision; enc = aui.record.encoding; } } /* * If the rate that the hardware is using is different than * the requested rate, scale buffer sizes so they will be the * same time duration as what was requested. This just gets * the rates to use for scaling, that actual scaling is done * later. */ rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate : aui.play.sample_rate; req_rate = rate; if (par->rate && par->rate != ~0U) req_rate = par->rate; /* * if block size and buffer size are not both set then * set the blocksize to half the buffer size */ bufsz = par->appbufsz; round = par->round; if (bufsz != ~0U) { bufsz = bufsz * rate / req_rate; if (round == ~0U) round = (bufsz + 1) / 2; else round = round * rate / req_rate; } else if (round != ~0U) { round = round * rate / req_rate; bufsz = round * 2; } else return 1; /* * get the play/record frame size in bytes */ if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_setpar: GETINFO"); hdl->sio.eof = 1; return 0; } ibpf = (hdl->sio.mode & SIO_REC) ? aui.record.channels * aui.record.bps : 1; obpf = (hdl->sio.mode & SIO_PLAY) ? aui.play.channels * aui.play.bps : 1; DPRINTFN(2, "sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf); /* * try to set parameters until the device accepts * a common block size for play and record */ for (i = 0; i < NRETRIES; i++) { AUDIO_INITINFO(&aui); aui.hiwat = (bufsz + round - 1) / round; aui.lowat = aui.hiwat; if (hdl->sio.mode & SIO_REC) aui.record.block_size = round * ibpf; if (hdl->sio.mode & SIO_PLAY) aui.play.block_size = round * obpf; if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { DPERROR("sio_sun_setpar2: SETINFO"); hdl->sio.eof = 1; return 0; } if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { DPERROR("sio_sun_setpar2: GETINFO"); hdl->sio.eof = 1; return 0; } infr = aui.record.block_size / ibpf; onfr = aui.play.block_size / obpf; DPRINTFN(2, "sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n", i, round, infr, onfr); /* * if half-duplex or both block sizes match, we're done */ if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) { DPRINTFN(2, "sio_sun_setpar: blocksize ok\n"); return 1; } /* * half of the retries, retry with the smaller value, * then with the larger returned value */ if (i < NRETRIES / 2) round = infr < onfr ? infr : onfr; else round = infr < onfr ? onfr : infr; } DPRINTFN(2, "sio_sun_setpar: couldn't find a working blocksize\n"); hdl->sio.eof = 1; return 0; #undef NRETRIES }