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