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