1 /* $OpenBSD: sio.c,v 1.14 2013/08/24 12:32:34 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 #ifdef DEBUG 115 if (!sio_getpar(hdl, &hdl->par)) 116 return 0; 117 hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->cpos = 0; 118 clock_gettime(CLOCK_MONOTONIC, &ts); 119 hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec; 120 #endif 121 hdl->rdrop = hdl->wsil = 0; 122 if (!hdl->ops->start(hdl)) 123 return 0; 124 hdl->started = 1; 125 return 1; 126 } 127 128 int 129 sio_stop(struct sio_hdl *hdl) 130 { 131 if (hdl->eof) { 132 DPRINTF("sio_stop: eof\n"); 133 return 0; 134 } 135 if (!hdl->started) { 136 DPRINTF("sio_stop: not started\n"); 137 hdl->eof = 1; 138 return 0; 139 } 140 if (!hdl->ops->stop(hdl)) 141 return 0; 142 #ifdef DEBUG 143 DPRINTF("libsndio: polls: %llu, written = %llu, read: %llu\n", 144 hdl->pollcnt, hdl->wcnt, hdl->rcnt); 145 #endif 146 hdl->started = 0; 147 return 1; 148 } 149 150 int 151 sio_setpar(struct sio_hdl *hdl, struct sio_par *par) 152 { 153 if (hdl->eof) { 154 DPRINTF("sio_setpar: eof\n"); 155 return 0; 156 } 157 if (par->__magic != SIO_PAR_MAGIC) { 158 DPRINTF("sio_setpar: use of uninitialized sio_par structure\n"); 159 hdl->eof = 1; 160 return 0; 161 } 162 if (hdl->started) { 163 DPRINTF("sio_setpar: already started\n"); 164 hdl->eof = 1; 165 return 0; 166 } 167 if (par->bufsz != ~0U) { 168 DPRINTF("sio_setpar: setting bufsz is deprecated\n"); 169 par->appbufsz = par->bufsz; 170 par->bufsz = ~0U; 171 } 172 if (par->rate != ~0U && par->appbufsz == ~0U) 173 par->appbufsz = par->rate * 200 / 1000; 174 return hdl->ops->setpar(hdl, par); 175 } 176 177 int 178 sio_getpar(struct sio_hdl *hdl, struct sio_par *par) 179 { 180 if (hdl->eof) { 181 DPRINTF("sio_getpar: eof\n"); 182 return 0; 183 } 184 if (hdl->started) { 185 DPRINTF("sio_getpar: already started\n"); 186 hdl->eof = 1; 187 return 0; 188 } 189 if (!hdl->ops->getpar(hdl, par)) { 190 par->__magic = 0; 191 return 0; 192 } 193 par->__magic = 0; 194 return 1; 195 } 196 197 int 198 sio_getcap(struct sio_hdl *hdl, struct sio_cap *cap) 199 { 200 if (hdl->eof) { 201 DPRINTF("sio_getcap: eof\n"); 202 return 0; 203 } 204 if (hdl->started) { 205 DPRINTF("sio_getcap: already started\n"); 206 hdl->eof = 1; 207 return 0; 208 } 209 return hdl->ops->getcap(hdl, cap); 210 } 211 212 static int 213 sio_psleep(struct sio_hdl *hdl, int event) 214 { 215 struct pollfd pfd[SIO_MAXNFDS]; 216 int revents; 217 int nfds; 218 219 nfds = sio_nfds(hdl); 220 if (nfds > SIO_MAXNFDS) { 221 DPRINTF("sio_psleep: %d: too many descriptors\n", nfds); 222 hdl->eof = 1; 223 return 0; 224 } 225 for (;;) { 226 nfds = sio_pollfd(hdl, pfd, event); 227 while (poll(pfd, nfds, -1) < 0) { 228 if (errno == EINTR) 229 continue; 230 DPERROR("sio_psleep: poll"); 231 hdl->eof = 1; 232 return 0; 233 } 234 revents = sio_revents(hdl, pfd); 235 if (revents & POLLHUP) { 236 DPRINTF("sio_psleep: hang-up\n"); 237 return 0; 238 } 239 if (revents & event) 240 break; 241 } 242 return 1; 243 } 244 245 static int 246 sio_rdrop(struct sio_hdl *hdl) 247 { 248 #define DROP_NMAX 0x1000 249 static char dummy[DROP_NMAX]; 250 ssize_t n, todo; 251 252 while (hdl->rdrop > 0) { 253 todo = hdl->rdrop; 254 if (todo > DROP_NMAX) 255 todo = DROP_NMAX; 256 n = hdl->ops->read(hdl, dummy, todo); 257 if (n == 0) 258 return 0; 259 hdl->rdrop -= n; 260 DPRINTF("sio_rdrop: dropped %zu bytes\n", n); 261 } 262 return 1; 263 } 264 265 static int 266 sio_wsil(struct sio_hdl *hdl) 267 { 268 #define ZERO_NMAX 0x1000 269 static char zero[ZERO_NMAX]; 270 ssize_t n, todo; 271 272 while (hdl->wsil > 0) { 273 todo = hdl->wsil; 274 if (todo > ZERO_NMAX) 275 todo = ZERO_NMAX; 276 n = hdl->ops->write(hdl, zero, todo); 277 if (n == 0) 278 return 0; 279 hdl->wsil -= n; 280 DPRINTF("sio_wsil: inserted %zu bytes\n", n); 281 } 282 return 1; 283 } 284 285 size_t 286 sio_read(struct sio_hdl *hdl, void *buf, size_t len) 287 { 288 unsigned int n; 289 char *data = buf; 290 size_t todo = len; 291 292 if (hdl->eof) { 293 DPRINTF("sio_read: eof\n"); 294 return 0; 295 } 296 if (!hdl->started || !(hdl->mode & SIO_REC)) { 297 DPRINTF("sio_read: recording not started\n"); 298 hdl->eof = 1; 299 return 0; 300 } 301 if (todo == 0) { 302 DPRINTF("sio_read: zero length read ignored\n"); 303 return 0; 304 } 305 if (!sio_rdrop(hdl)) 306 return 0; 307 while (todo > 0) { 308 n = hdl->ops->read(hdl, data, todo); 309 if (n == 0) { 310 if (hdl->nbio || hdl->eof || todo < len) 311 break; 312 if (!sio_psleep(hdl, POLLIN)) 313 break; 314 continue; 315 } 316 data += n; 317 todo -= n; 318 #ifdef DEBUG 319 hdl->rcnt += n; 320 #endif 321 } 322 return len - todo; 323 } 324 325 size_t 326 sio_write(struct sio_hdl *hdl, const void *buf, size_t len) 327 { 328 unsigned int n; 329 const unsigned char *data = buf; 330 size_t todo = len; 331 332 if (hdl->eof) { 333 DPRINTF("sio_write: eof\n"); 334 return 0; 335 } 336 if (!hdl->started || !(hdl->mode & SIO_PLAY)) { 337 DPRINTF("sio_write: playback not started\n"); 338 hdl->eof = 1; 339 return 0; 340 } 341 if (todo == 0) { 342 DPRINTF("sio_write: zero length write ignored\n"); 343 return 0; 344 } 345 if (!sio_wsil(hdl)) 346 return 0; 347 while (todo > 0) { 348 n = hdl->ops->write(hdl, data, todo); 349 if (n == 0) { 350 if (hdl->nbio || hdl->eof) 351 break; 352 if (!sio_psleep(hdl, POLLOUT)) 353 break; 354 continue; 355 } 356 data += n; 357 todo -= n; 358 #ifdef DEBUG 359 hdl->wcnt += n; 360 #endif 361 } 362 return len - todo; 363 } 364 365 int 366 sio_nfds(struct sio_hdl *hdl) 367 { 368 return hdl->ops->nfds(hdl); 369 } 370 371 int 372 sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events) 373 { 374 if (hdl->eof) 375 return 0; 376 if (!hdl->started) 377 events = 0; 378 return hdl->ops->pollfd(hdl, pfd, events); 379 } 380 381 int 382 sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) 383 { 384 int revents; 385 #ifdef DEBUG 386 struct timespec ts0, ts1; 387 388 if (sndio_debug >= 2) 389 clock_gettime(CLOCK_MONOTONIC, &ts0); 390 #endif 391 if (hdl->eof) 392 return POLLHUP; 393 #ifdef DEBUG 394 hdl->pollcnt++; 395 #endif 396 revents = hdl->ops->revents(hdl, pfd); 397 if (!hdl->started) 398 return revents & POLLHUP; 399 #ifdef DEBUG 400 if (sndio_debug >= 3) { 401 clock_gettime(CLOCK_MONOTONIC, &ts1); 402 DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n", 403 1000000000LL * ts0.tv_sec + 404 ts0.tv_nsec - hdl->start_nsec, 405 revents, 406 1000000000LL * (ts1.tv_sec - ts0.tv_sec) + 407 ts1.tv_nsec - ts0.tv_nsec); 408 } 409 #endif 410 if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl)) 411 revents &= ~POLLOUT; 412 if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl)) 413 revents &= ~POLLIN; 414 return revents; 415 } 416 417 int 418 sio_eof(struct sio_hdl *hdl) 419 { 420 return hdl->eof; 421 } 422 423 void 424 sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) 425 { 426 if (hdl->started) { 427 DPRINTF("sio_onmove: already started\n"); 428 hdl->eof = 1; 429 return; 430 } 431 hdl->move_cb = cb; 432 hdl->move_addr = addr; 433 } 434 435 #ifdef DEBUG 436 void 437 sio_printpos(struct sio_hdl *hdl) 438 { 439 struct timespec ts; 440 long long rpos, rdiff; 441 long long cpos, cdiff; 442 long long wpos, wdiff; 443 444 clock_gettime(CLOCK_MONOTONIC, &ts); 445 446 rpos = (hdl->mode & SIO_REC) ? 447 hdl->rcnt / (hdl->par.bps * hdl->par.rchan) : 0; 448 wpos = (hdl->mode & SIO_PLAY) ? 449 hdl->wcnt / (hdl->par.bps * hdl->par.pchan) : 0; 450 451 cdiff = hdl->cpos % hdl->par.round; 452 cpos = hdl->cpos / hdl->par.round; 453 if (cdiff > hdl->par.round / 2) { 454 cpos++; 455 cdiff = cdiff - hdl->par.round; 456 } 457 rdiff = rpos % hdl->par.round; 458 rpos = rpos / hdl->par.round; 459 if (rdiff > hdl->par.round / 2) { 460 rpos++; 461 rdiff = rdiff - hdl->par.round; 462 } 463 wdiff = wpos % hdl->par.round; 464 wpos = wpos / hdl->par.round; 465 if (wdiff > hdl->par.round / 2) { 466 wpos++; 467 wdiff = wdiff - hdl->par.round; 468 } 469 DPRINTF("%011lld: " 470 "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n", 471 1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec, 472 cpos, cdiff, wpos, wdiff, rpos, rdiff); 473 } 474 #endif 475 476 void 477 sio_onmove_cb(struct sio_hdl *hdl, int delta) 478 { 479 #ifdef DEBUG 480 hdl->cpos += delta; 481 if (sndio_debug >= 2) 482 sio_printpos(hdl); 483 #endif 484 if (hdl->move_cb) 485 hdl->move_cb(hdl->move_addr, delta); 486 } 487 488 int 489 sio_setvol(struct sio_hdl *hdl, unsigned int ctl) 490 { 491 if (hdl->eof) 492 return 0; 493 if (!hdl->ops->setvol) 494 return 1; 495 if (!hdl->ops->setvol(hdl, ctl)) 496 return 0; 497 hdl->ops->getvol(hdl); 498 return 1; 499 } 500 501 int 502 sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned int), void *addr) 503 { 504 if (hdl->started) { 505 DPRINTF("sio_onvol: already started\n"); 506 hdl->eof = 1; 507 return 0; 508 } 509 if (!hdl->ops->setvol) 510 return 0; 511 hdl->vol_cb = cb; 512 hdl->vol_addr = addr; 513 hdl->ops->getvol(hdl); 514 return 1; 515 } 516 517 void 518 sio_onvol_cb(struct sio_hdl *hdl, unsigned int ctl) 519 { 520 if (hdl->vol_cb) 521 hdl->vol_cb(hdl->vol_addr, ctl); 522 } 523