1 /* $OpenBSD: sio.c,v 1.24 2019/06/29 06:05:26 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/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) == -1) { 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 while (todo > 0) { 298 if (!sio_rdrop(hdl)) 299 return 0; 300 maxread = hdl->rused; 301 if (maxread > todo) 302 maxread = todo; 303 n = maxread > 0 ? hdl->ops->read(hdl, data, maxread) : 0; 304 if (n == 0) { 305 if (hdl->nbio || hdl->eof || todo < len) 306 break; 307 if (!sio_psleep(hdl, POLLIN)) 308 break; 309 continue; 310 } 311 data += n; 312 todo -= n; 313 hdl->rused -= n; 314 } 315 return len - todo; 316 } 317 318 size_t 319 sio_write(struct sio_hdl *hdl, const void *buf, size_t len) 320 { 321 unsigned int n; 322 const unsigned char *data = buf; 323 size_t todo = len, maxwrite; 324 325 if (hdl->eof) { 326 DPRINTF("sio_write: eof\n"); 327 return 0; 328 } 329 if (!hdl->started || !(hdl->mode & SIO_PLAY)) { 330 DPRINTF("sio_write: playback not started\n"); 331 hdl->eof = 1; 332 return 0; 333 } 334 while (todo > 0) { 335 if (!sio_wsil(hdl)) 336 return 0; 337 maxwrite = hdl->par.bufsz * hdl->par.pchan * hdl->par.bps - 338 hdl->wused; 339 if (maxwrite > todo) 340 maxwrite = todo; 341 n = maxwrite > 0 ? hdl->ops->write(hdl, data, maxwrite) : 0; 342 if (n == 0) { 343 if (hdl->nbio || hdl->eof) 344 break; 345 if (!sio_psleep(hdl, POLLOUT)) 346 break; 347 continue; 348 } 349 data += n; 350 todo -= n; 351 hdl->wused += n; 352 } 353 return len - todo; 354 } 355 356 int 357 sio_nfds(struct sio_hdl *hdl) 358 { 359 return hdl->ops->nfds(hdl); 360 } 361 362 int 363 sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events) 364 { 365 if (hdl->eof) 366 return 0; 367 if (!hdl->started) 368 events = 0; 369 return hdl->ops->pollfd(hdl, pfd, events); 370 } 371 372 int 373 sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) 374 { 375 int revents; 376 #ifdef DEBUG 377 struct timespec ts0, ts1; 378 379 if (_sndio_debug >= 4) 380 clock_gettime(CLOCK_MONOTONIC, &ts0); 381 #endif 382 if (hdl->eof) 383 return POLLHUP; 384 #ifdef DEBUG 385 hdl->pollcnt++; 386 #endif 387 revents = hdl->ops->revents(hdl, pfd); 388 if (!hdl->started) 389 return revents & POLLHUP; 390 #ifdef DEBUG 391 if (_sndio_debug >= 4) { 392 clock_gettime(CLOCK_MONOTONIC, &ts1); 393 DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n", 394 1000000000LL * ts0.tv_sec + 395 ts0.tv_nsec - hdl->start_nsec, 396 revents, 397 1000000000LL * (ts1.tv_sec - ts0.tv_sec) + 398 ts1.tv_nsec - ts0.tv_nsec); 399 } 400 #endif 401 if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl)) 402 revents &= ~POLLOUT; 403 if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl)) 404 revents &= ~POLLIN; 405 return revents; 406 } 407 408 int 409 sio_eof(struct sio_hdl *hdl) 410 { 411 return hdl->eof; 412 } 413 414 void 415 sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) 416 { 417 if (hdl->started) { 418 DPRINTF("sio_onmove: already started\n"); 419 hdl->eof = 1; 420 return; 421 } 422 hdl->move_cb = cb; 423 hdl->move_addr = addr; 424 } 425 426 #ifdef DEBUG 427 void 428 _sio_printpos(struct sio_hdl *hdl) 429 { 430 struct timespec ts; 431 long long rpos, rdiff; 432 long long cpos, cdiff; 433 long long wpos, wdiff; 434 unsigned rbpf, wbpf, rround, wround; 435 436 clock_gettime(CLOCK_MONOTONIC, &ts); 437 rbpf = (hdl->mode & SIO_REC) ? hdl->par.bps * hdl->par.rchan : 1; 438 wbpf = (hdl->mode & SIO_PLAY) ? hdl->par.bps * hdl->par.pchan : 1; 439 rround = hdl->par.round * rbpf; 440 wround = hdl->par.round * wbpf; 441 442 rpos = (hdl->mode & SIO_REC) ? 443 hdl->cpos * rbpf - hdl->rused : 0; 444 wpos = (hdl->mode & SIO_PLAY) ? 445 hdl->cpos * wbpf + hdl->wused : 0; 446 447 cdiff = hdl->cpos % hdl->par.round; 448 cpos = hdl->cpos / hdl->par.round; 449 if (cdiff > hdl->par.round / 2) { 450 cpos++; 451 cdiff = cdiff - hdl->par.round; 452 } 453 rdiff = rpos % rround; 454 rpos = rpos / rround; 455 if (rdiff > rround / 2) { 456 rpos++; 457 rdiff = rdiff - rround; 458 } 459 wdiff = wpos % wround; 460 wpos = wpos / wround; 461 if (wdiff > wround / 2) { 462 wpos++; 463 wdiff = wdiff - wround; 464 } 465 DPRINTF("%011lld: " 466 "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n", 467 1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec, 468 cpos, cdiff, wpos, wdiff, rpos, rdiff); 469 } 470 #endif 471 472 void 473 _sio_onmove_cb(struct sio_hdl *hdl, int delta) 474 { 475 hdl->cpos += delta; 476 if (hdl->mode & SIO_REC) 477 hdl->rused += delta * (hdl->par.bps * hdl->par.rchan); 478 if (hdl->mode & SIO_PLAY) 479 hdl->wused -= delta * (hdl->par.bps * hdl->par.pchan); 480 #ifdef DEBUG 481 if (_sndio_debug >= 3) 482 _sio_printpos(hdl); 483 if ((hdl->mode & SIO_PLAY) && hdl->wused < 0) { 484 DPRINTFN(1, "sndio: h/w failure: negative buffer usage\n"); 485 hdl->eof = 1; 486 return; 487 } 488 #endif 489 if (hdl->move_cb) 490 hdl->move_cb(hdl->move_addr, delta); 491 } 492 493 int 494 sio_setvol(struct sio_hdl *hdl, unsigned int ctl) 495 { 496 if (hdl->eof) 497 return 0; 498 if (!hdl->ops->setvol) 499 return 1; 500 if (!hdl->ops->setvol(hdl, ctl)) 501 return 0; 502 hdl->ops->getvol(hdl); 503 return 1; 504 } 505 506 int 507 sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned int), void *addr) 508 { 509 if (hdl->started) { 510 DPRINTF("sio_onvol: already started\n"); 511 hdl->eof = 1; 512 return 0; 513 } 514 if (!hdl->ops->setvol) 515 return 0; 516 hdl->vol_cb = cb; 517 hdl->vol_addr = addr; 518 hdl->ops->getvol(hdl); 519 return 1; 520 } 521 522 void 523 _sio_onvol_cb(struct sio_hdl *hdl, unsigned int ctl) 524 { 525 if (hdl->vol_cb) 526 hdl->vol_cb(hdl->vol_addr, ctl); 527 } 528