1 /* $OpenBSD: sio.c,v 1.19 2015/01/16 16:48:52 deraadt 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/stat.h> 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <poll.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <time.h> 28 #include <unistd.h> 29 30 #include "debug.h" 31 #include "sio_priv.h" 32 33 #define SIO_PAR_MAGIC 0x83b905a4 34 35 void 36 sio_initpar(struct sio_par *par) 37 { 38 memset(par, 0xff, sizeof(struct sio_par)); 39 par->__magic = SIO_PAR_MAGIC; 40 } 41 42 struct sio_hdl * 43 sio_open(const char *str, unsigned int mode, int nbio) 44 { 45 static char devany[] = SIO_DEVANY; 46 struct sio_hdl *hdl; 47 const char *p; 48 49 #ifdef DEBUG 50 _sndio_debug_init(); 51 #endif 52 if ((mode & (SIO_PLAY | SIO_REC)) == 0) 53 return NULL; 54 if (str == NULL) /* backward compat */ 55 str = devany; 56 if (strcmp(str, devany) == 0 && !issetugid()) { 57 str = getenv("AUDIODEVICE"); 58 if (str == NULL) 59 str = devany; 60 } 61 if (strcmp(str, devany) == 0) { 62 hdl = _sio_aucat_open("/0", mode, nbio); 63 if (hdl != NULL) 64 return hdl; 65 return _sio_sun_open("/0", mode, nbio); 66 } 67 if ((p = _sndio_parsetype(str, "snd")) != NULL || 68 (p = _sndio_parsetype(str, "aucat")) != NULL) 69 return _sio_aucat_open(p, mode, nbio); 70 if ((p = _sndio_parsetype(str, "rsnd")) != NULL || 71 (p = _sndio_parsetype(str, "sun")) != NULL) { 72 return _sio_sun_open(p, mode, nbio); 73 } 74 DPRINTF("sio_open: %s: unknown device type\n", str); 75 return NULL; 76 } 77 78 void 79 _sio_create(struct sio_hdl *hdl, struct sio_ops *ops, 80 unsigned int mode, int nbio) 81 { 82 hdl->ops = ops; 83 hdl->mode = mode; 84 hdl->nbio = nbio; 85 hdl->started = 0; 86 hdl->eof = 0; 87 hdl->move_cb = NULL; 88 hdl->vol_cb = NULL; 89 } 90 91 void 92 sio_close(struct sio_hdl *hdl) 93 { 94 hdl->ops->close(hdl); 95 } 96 97 int 98 sio_start(struct sio_hdl *hdl) 99 { 100 #ifdef DEBUG 101 struct timespec ts; 102 #endif 103 104 if (hdl->eof) { 105 DPRINTF("sio_start: eof\n"); 106 return 0; 107 } 108 if (hdl->started) { 109 DPRINTF("sio_start: already started\n"); 110 hdl->eof = 1; 111 return 0; 112 } 113 hdl->cpos = 0; 114 hdl->rused = hdl->wused = 0; 115 if (!sio_getpar(hdl, &hdl->par)) 116 return 0; 117 #ifdef DEBUG 118 hdl->pollcnt = 0; 119 clock_gettime(CLOCK_MONOTONIC, &ts); 120 hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec; 121 #endif 122 hdl->rdrop = hdl->wsil = 0; 123 if (!hdl->ops->start(hdl)) 124 return 0; 125 hdl->started = 1; 126 return 1; 127 } 128 129 int 130 sio_stop(struct sio_hdl *hdl) 131 { 132 if (hdl->eof) { 133 DPRINTF("sio_stop: eof\n"); 134 return 0; 135 } 136 if (!hdl->started) { 137 DPRINTF("sio_stop: not started\n"); 138 hdl->eof = 1; 139 return 0; 140 } 141 if (!hdl->ops->stop(hdl)) 142 return 0; 143 #ifdef DEBUG 144 DPRINTFN(2, "libsndio: polls: %llu, samples = %llu\n", 145 hdl->pollcnt, hdl->cpos); 146 #endif 147 hdl->started = 0; 148 return 1; 149 } 150 151 int 152 sio_setpar(struct sio_hdl *hdl, struct sio_par *par) 153 { 154 if (hdl->eof) { 155 DPRINTF("sio_setpar: eof\n"); 156 return 0; 157 } 158 if (par->__magic != SIO_PAR_MAGIC) { 159 DPRINTF("sio_setpar: use of uninitialized sio_par structure\n"); 160 hdl->eof = 1; 161 return 0; 162 } 163 if (hdl->started) { 164 DPRINTF("sio_setpar: already started\n"); 165 hdl->eof = 1; 166 return 0; 167 } 168 if (par->bufsz != ~0U) { 169 DPRINTF("sio_setpar: setting bufsz is deprecated\n"); 170 par->appbufsz = par->bufsz; 171 par->bufsz = ~0U; 172 } 173 if (par->rate != ~0U && par->appbufsz == ~0U) 174 par->appbufsz = par->rate * 200 / 1000; 175 return hdl->ops->setpar(hdl, par); 176 } 177 178 int 179 sio_getpar(struct sio_hdl *hdl, struct sio_par *par) 180 { 181 if (hdl->eof) { 182 DPRINTF("sio_getpar: eof\n"); 183 return 0; 184 } 185 if (hdl->started) { 186 DPRINTF("sio_getpar: already started\n"); 187 hdl->eof = 1; 188 return 0; 189 } 190 if (!hdl->ops->getpar(hdl, par)) { 191 par->__magic = 0; 192 return 0; 193 } 194 par->__magic = 0; 195 return 1; 196 } 197 198 int 199 sio_getcap(struct sio_hdl *hdl, struct sio_cap *cap) 200 { 201 if (hdl->eof) { 202 DPRINTF("sio_getcap: eof\n"); 203 return 0; 204 } 205 if (hdl->started) { 206 DPRINTF("sio_getcap: already started\n"); 207 hdl->eof = 1; 208 return 0; 209 } 210 return hdl->ops->getcap(hdl, cap); 211 } 212 213 static int 214 sio_psleep(struct sio_hdl *hdl, int event) 215 { 216 struct pollfd pfd[SIO_MAXNFDS]; 217 int revents; 218 int nfds; 219 220 nfds = sio_nfds(hdl); 221 if (nfds > SIO_MAXNFDS) { 222 DPRINTF("sio_psleep: %d: too many descriptors\n", nfds); 223 hdl->eof = 1; 224 return 0; 225 } 226 for (;;) { 227 nfds = sio_pollfd(hdl, pfd, event); 228 while (poll(pfd, nfds, -1) < 0) { 229 if (errno == EINTR) 230 continue; 231 DPERROR("sio_psleep: poll"); 232 hdl->eof = 1; 233 return 0; 234 } 235 revents = sio_revents(hdl, pfd); 236 if (revents & POLLHUP) { 237 DPRINTF("sio_psleep: hang-up\n"); 238 return 0; 239 } 240 if (revents & event) 241 break; 242 } 243 return 1; 244 } 245 246 static int 247 sio_rdrop(struct sio_hdl *hdl) 248 { 249 #define DROP_NMAX 0x1000 250 static char dummy[DROP_NMAX]; 251 ssize_t n, todo; 252 253 while (hdl->rdrop > 0) { 254 todo = hdl->rdrop; 255 if (todo > DROP_NMAX) 256 todo = DROP_NMAX; 257 n = hdl->ops->read(hdl, dummy, todo); 258 if (n == 0) 259 return 0; 260 hdl->rdrop -= n; 261 DPRINTF("sio_rdrop: dropped %zu bytes\n", n); 262 } 263 return 1; 264 } 265 266 static int 267 sio_wsil(struct sio_hdl *hdl) 268 { 269 #define ZERO_NMAX 0x1000 270 static char zero[ZERO_NMAX]; 271 ssize_t n, todo; 272 273 while (hdl->wsil > 0) { 274 todo = hdl->wsil; 275 if (todo > ZERO_NMAX) 276 todo = ZERO_NMAX; 277 n = hdl->ops->write(hdl, zero, todo); 278 if (n == 0) 279 return 0; 280 hdl->wsil -= n; 281 DPRINTF("sio_wsil: inserted %zu bytes\n", n); 282 } 283 return 1; 284 } 285 286 size_t 287 sio_read(struct sio_hdl *hdl, void *buf, size_t len) 288 { 289 unsigned int n; 290 char *data = buf; 291 size_t todo = len, maxread; 292 293 if (hdl->eof) { 294 DPRINTF("sio_read: eof\n"); 295 return 0; 296 } 297 if (!hdl->started || !(hdl->mode & SIO_REC)) { 298 DPRINTF("sio_read: recording not started\n"); 299 hdl->eof = 1; 300 return 0; 301 } 302 if (todo == 0) { 303 DPRINTF("sio_read: zero length read ignored\n"); 304 return 0; 305 } 306 while (todo > 0) { 307 if (!sio_rdrop(hdl)) 308 return 0; 309 maxread = hdl->rused; 310 if (maxread > todo) 311 maxread = todo; 312 n = maxread > 0 ? hdl->ops->read(hdl, data, maxread) : 0; 313 if (n == 0) { 314 if (hdl->nbio || hdl->eof || todo < len) 315 break; 316 if (!sio_psleep(hdl, POLLIN)) 317 break; 318 continue; 319 } 320 data += n; 321 todo -= n; 322 hdl->rused -= n; 323 } 324 return len - todo; 325 } 326 327 size_t 328 sio_write(struct sio_hdl *hdl, const void *buf, size_t len) 329 { 330 unsigned int n; 331 const unsigned char *data = buf; 332 size_t todo = len, maxwrite; 333 334 if (hdl->eof) { 335 DPRINTF("sio_write: eof\n"); 336 return 0; 337 } 338 if (!hdl->started || !(hdl->mode & SIO_PLAY)) { 339 DPRINTF("sio_write: playback not started\n"); 340 hdl->eof = 1; 341 return 0; 342 } 343 if (todo == 0) { 344 DPRINTF("sio_write: zero length write ignored\n"); 345 return 0; 346 } 347 while (todo > 0) { 348 if (!sio_wsil(hdl)) 349 return 0; 350 maxwrite = hdl->par.bufsz * hdl->par.pchan * hdl->par.bps - 351 hdl->wused; 352 if (maxwrite > todo) 353 maxwrite = todo; 354 n = maxwrite > 0 ? hdl->ops->write(hdl, data, maxwrite) : 0; 355 if (n == 0) { 356 if (hdl->nbio || hdl->eof) 357 break; 358 if (!sio_psleep(hdl, POLLOUT)) 359 break; 360 continue; 361 } 362 data += n; 363 todo -= n; 364 hdl->wused += n; 365 } 366 return len - todo; 367 } 368 369 int 370 sio_nfds(struct sio_hdl *hdl) 371 { 372 return hdl->ops->nfds(hdl); 373 } 374 375 int 376 sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events) 377 { 378 if (hdl->eof) 379 return 0; 380 if (!hdl->started) 381 events = 0; 382 return hdl->ops->pollfd(hdl, pfd, events); 383 } 384 385 int 386 sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) 387 { 388 int revents; 389 #ifdef DEBUG 390 struct timespec ts0, ts1; 391 392 if (_sndio_debug >= 4) 393 clock_gettime(CLOCK_MONOTONIC, &ts0); 394 #endif 395 if (hdl->eof) 396 return POLLHUP; 397 #ifdef DEBUG 398 hdl->pollcnt++; 399 #endif 400 revents = hdl->ops->revents(hdl, pfd); 401 if (!hdl->started) 402 return revents & POLLHUP; 403 #ifdef DEBUG 404 if (_sndio_debug >= 4) { 405 clock_gettime(CLOCK_MONOTONIC, &ts1); 406 DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n", 407 1000000000LL * ts0.tv_sec + 408 ts0.tv_nsec - hdl->start_nsec, 409 revents, 410 1000000000LL * (ts1.tv_sec - ts0.tv_sec) + 411 ts1.tv_nsec - ts0.tv_nsec); 412 } 413 #endif 414 if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl)) 415 revents &= ~POLLOUT; 416 if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl)) 417 revents &= ~POLLIN; 418 return revents; 419 } 420 421 int 422 sio_eof(struct sio_hdl *hdl) 423 { 424 return hdl->eof; 425 } 426 427 void 428 sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) 429 { 430 if (hdl->started) { 431 DPRINTF("sio_onmove: already started\n"); 432 hdl->eof = 1; 433 return; 434 } 435 hdl->move_cb = cb; 436 hdl->move_addr = addr; 437 } 438 439 #ifdef DEBUG 440 void 441 _sio_printpos(struct sio_hdl *hdl) 442 { 443 struct timespec ts; 444 long long rpos, rdiff; 445 long long cpos, cdiff; 446 long long wpos, wdiff; 447 unsigned rbpf, wbpf, rround, wround; 448 449 clock_gettime(CLOCK_MONOTONIC, &ts); 450 rbpf = (hdl->mode & SIO_REC) ? hdl->par.bps * hdl->par.rchan : 1; 451 wbpf = (hdl->mode & SIO_PLAY) ? hdl->par.bps * hdl->par.pchan : 1; 452 rround = hdl->par.round * rbpf; 453 wround = hdl->par.round * wbpf; 454 455 rpos = (hdl->mode & SIO_REC) ? 456 hdl->cpos * rbpf - hdl->rused : 0; 457 wpos = (hdl->mode & SIO_PLAY) ? 458 hdl->cpos * wbpf + hdl->wused : 0; 459 460 cdiff = hdl->cpos % hdl->par.round; 461 cpos = hdl->cpos / hdl->par.round; 462 if (cdiff > hdl->par.round / 2) { 463 cpos++; 464 cdiff = cdiff - hdl->par.round; 465 } 466 rdiff = rpos % rround; 467 rpos = rpos / rround; 468 if (rdiff > rround / 2) { 469 rpos++; 470 rdiff = rdiff - rround; 471 } 472 wdiff = wpos % wround; 473 wpos = wpos / wround; 474 if (wdiff > wround / 2) { 475 wpos++; 476 wdiff = wdiff - wround; 477 } 478 DPRINTF("%011lld: " 479 "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n", 480 1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec, 481 cpos, cdiff, wpos, wdiff, rpos, rdiff); 482 } 483 #endif 484 485 void 486 _sio_onmove_cb(struct sio_hdl *hdl, int delta) 487 { 488 hdl->cpos += delta; 489 if (hdl->mode & SIO_REC) 490 hdl->rused += delta * (hdl->par.bps * hdl->par.rchan); 491 if (hdl->mode & SIO_PLAY) 492 hdl->wused -= delta * (hdl->par.bps * hdl->par.pchan); 493 #ifdef DEBUG 494 if (_sndio_debug >= 3) 495 _sio_printpos(hdl); 496 if ((hdl->mode & SIO_PLAY) && hdl->wused < 0) { 497 DPRINTFN(1, "sndio: h/w failure: negative buffer usage\n"); 498 hdl->eof = 1; 499 return; 500 } 501 #endif 502 if (hdl->move_cb) 503 hdl->move_cb(hdl->move_addr, delta); 504 } 505 506 int 507 sio_setvol(struct sio_hdl *hdl, unsigned int ctl) 508 { 509 if (hdl->eof) 510 return 0; 511 if (!hdl->ops->setvol) 512 return 1; 513 if (!hdl->ops->setvol(hdl, ctl)) 514 return 0; 515 hdl->ops->getvol(hdl); 516 return 1; 517 } 518 519 int 520 sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned int), void *addr) 521 { 522 if (hdl->started) { 523 DPRINTF("sio_onvol: already started\n"); 524 hdl->eof = 1; 525 return 0; 526 } 527 if (!hdl->ops->setvol) 528 return 0; 529 hdl->vol_cb = cb; 530 hdl->vol_addr = addr; 531 hdl->ops->getvol(hdl); 532 return 1; 533 } 534 535 void 536 _sio_onvol_cb(struct sio_hdl *hdl, unsigned int ctl) 537 { 538 if (hdl->vol_cb) 539 hdl->vol_cb(hdl->vol_addr, ctl); 540 } 541