1 /* $OpenBSD: sio.c,v 1.6 2011/05/09 17:34:14 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/time.h> 21 #include <sys/stat.h> 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <poll.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "debug.h" 32 #include "sio_priv.h" 33 34 #define SIO_PAR_MAGIC 0x83b905a4 35 36 struct sio_backend { 37 char *prefix; 38 struct sio_hdl *(*open)(const char *, unsigned, int); 39 }; 40 41 static struct sio_backend backends[] = { 42 { "aucat", sio_aucat_open }, 43 { "sun", sio_sun_open }, 44 { NULL, NULL } 45 }; 46 47 void 48 sio_initpar(struct sio_par *par) 49 { 50 memset(par, 0xff, sizeof(struct sio_par)); 51 par->__magic = SIO_PAR_MAGIC; 52 } 53 54 struct sio_hdl * 55 sio_open(const char *str, unsigned mode, int nbio) 56 { 57 struct sio_backend *b; 58 struct sio_hdl *hdl; 59 char *sep; 60 int len; 61 62 #ifdef DEBUG 63 sndio_debug_init(); 64 #endif 65 if ((mode & (SIO_PLAY | SIO_REC)) == 0) 66 return NULL; 67 if (str == NULL && !issetugid()) 68 str = getenv("AUDIODEVICE"); 69 if (str == NULL) { 70 for (b = backends; b->prefix != NULL; b++) { 71 hdl = b->open(NULL, mode, nbio); 72 if (hdl != NULL) 73 return hdl; 74 } 75 return NULL; 76 } 77 sep = strchr(str, ':'); 78 if (sep == NULL) { 79 DPRINTF("sio_open: %s: ':' missing in device name\n", str); 80 return NULL; 81 } 82 len = sep - str; 83 for (b = backends; b->prefix != NULL; b++) { 84 if (strlen(b->prefix) == len && memcmp(b->prefix, str, len) == 0) 85 return b->open(sep + 1, mode, nbio); 86 } 87 DPRINTF("sio_open: %s: unknown device type\n", str); 88 return NULL; 89 } 90 91 void 92 sio_create(struct sio_hdl *hdl, struct sio_ops *ops, unsigned mode, int nbio) 93 { 94 hdl->ops = ops; 95 hdl->mode = mode; 96 hdl->nbio = nbio; 97 hdl->started = 0; 98 hdl->eof = 0; 99 hdl->move_cb = NULL; 100 hdl->vol_cb = NULL; 101 } 102 103 void 104 sio_close(struct sio_hdl *hdl) 105 { 106 hdl->ops->close(hdl); 107 } 108 109 int 110 sio_start(struct sio_hdl *hdl) 111 { 112 if (hdl->eof) { 113 DPRINTF("sio_start: eof\n"); 114 return 0; 115 } 116 if (hdl->started) { 117 DPRINTF("sio_start: already started\n"); 118 hdl->eof = 1; 119 return 0; 120 } 121 #ifdef DEBUG 122 if (!sio_getpar(hdl, &hdl->par)) 123 return 0; 124 hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->realpos = 0; 125 gettimeofday(&hdl->tv, NULL); 126 #endif 127 if (!hdl->ops->start(hdl)) 128 return 0; 129 hdl->started = 1; 130 return 1; 131 } 132 133 int 134 sio_stop(struct sio_hdl *hdl) 135 { 136 if (hdl->eof) { 137 DPRINTF("sio_stop: eof\n"); 138 return 0; 139 } 140 if (!hdl->started) { 141 DPRINTF("sio_stop: not started\n"); 142 hdl->eof = 1; 143 return 0; 144 } 145 if (!hdl->ops->stop(hdl)) 146 return 0; 147 #ifdef DEBUG 148 DPRINTF("libsndio: polls: %llu, written = %llu, read: %llu\n", 149 hdl->pollcnt, hdl->wcnt, hdl->rcnt); 150 #endif 151 hdl->started = 0; 152 return 1; 153 } 154 155 int 156 sio_setpar(struct sio_hdl *hdl, struct sio_par *par) 157 { 158 if (hdl->eof) { 159 DPRINTF("sio_setpar: eof\n"); 160 return 0; 161 } 162 if (par->__magic != SIO_PAR_MAGIC) { 163 DPRINTF("sio_setpar: use of uninitialized sio_par structure\n"); 164 hdl->eof = 1; 165 return 0; 166 } 167 if (hdl->started) { 168 DPRINTF("sio_setpar: already started\n"); 169 hdl->eof = 1; 170 return 0; 171 } 172 if (par->bufsz != ~0U) { 173 DPRINTF("sio_setpar: setting bufsz is deprecated\n"); 174 par->appbufsz = par->bufsz; 175 } 176 if (par->rate != ~0U && par->appbufsz == ~0U) 177 par->appbufsz = par->rate * 200 / 1000; 178 return hdl->ops->setpar(hdl, par); 179 } 180 181 int 182 sio_getpar(struct sio_hdl *hdl, struct sio_par *par) 183 { 184 if (hdl->eof) { 185 DPRINTF("sio_getpar: eof\n"); 186 return 0; 187 } 188 if (hdl->started) { 189 DPRINTF("sio_getpar: already started\n"); 190 hdl->eof = 1; 191 return 0; 192 } 193 if (!hdl->ops->getpar(hdl, par)) { 194 par->__magic = 0; 195 return 0; 196 } 197 par->__magic = 0; 198 return 1; 199 } 200 201 int 202 sio_getcap(struct sio_hdl *hdl, struct sio_cap *cap) 203 { 204 if (hdl->eof) { 205 DPRINTF("sio_getcap: eof\n"); 206 return 0; 207 } 208 if (hdl->started) { 209 DPRINTF("sio_getcap: already started\n"); 210 hdl->eof = 1; 211 return 0; 212 } 213 return hdl->ops->getcap(hdl, cap); 214 } 215 216 static int 217 sio_psleep(struct sio_hdl *hdl, int event) 218 { 219 struct pollfd pfd[SIO_MAXNFDS]; 220 int revents; 221 nfds_t nfds; 222 223 nfds = sio_nfds(hdl); 224 for (;;) { 225 sio_pollfd(hdl, pfd, event); 226 while (poll(pfd, nfds, -1) < 0) { 227 if (errno == EINTR) 228 continue; 229 DPERROR("sio_psleep: poll"); 230 hdl->eof = 1; 231 return 0; 232 } 233 revents = sio_revents(hdl, pfd); 234 if (revents & POLLHUP) { 235 DPRINTF("sio_psleep: hang-up\n"); 236 return 0; 237 } 238 if (revents & event) 239 break; 240 } 241 return 1; 242 } 243 244 size_t 245 sio_read(struct sio_hdl *hdl, void *buf, size_t len) 246 { 247 unsigned n; 248 char *data = buf; 249 size_t todo = len; 250 251 if (hdl->eof) { 252 DPRINTF("sio_read: eof\n"); 253 return 0; 254 } 255 if (!hdl->started || !(hdl->mode & SIO_REC)) { 256 DPRINTF("sio_read: recording not started\n"); 257 hdl->eof = 1; 258 return 0; 259 } 260 if (todo == 0) { 261 DPRINTF("sio_read: zero length read ignored\n"); 262 return 0; 263 } 264 while (todo > 0) { 265 n = hdl->ops->read(hdl, data, todo); 266 if (n == 0) { 267 if (hdl->nbio || hdl->eof || todo < len) 268 break; 269 if (!sio_psleep(hdl, POLLIN)) 270 break; 271 continue; 272 } 273 data += n; 274 todo -= n; 275 #ifdef DEBUG 276 hdl->rcnt += n; 277 #endif 278 } 279 return len - todo; 280 } 281 282 size_t 283 sio_write(struct sio_hdl *hdl, const void *buf, size_t len) 284 { 285 unsigned n; 286 const unsigned char *data = buf; 287 size_t todo = len; 288 #ifdef DEBUG 289 struct timeval tv0, tv1, dtv; 290 unsigned us; 291 292 if (sndio_debug >= 2) 293 gettimeofday(&tv0, NULL); 294 #endif 295 296 if (hdl->eof) { 297 DPRINTF("sio_write: eof\n"); 298 return 0; 299 } 300 if (!hdl->started || !(hdl->mode & SIO_PLAY)) { 301 DPRINTF("sio_write: playback not started\n"); 302 hdl->eof = 1; 303 return 0; 304 } 305 if (todo == 0) { 306 DPRINTF("sio_write: zero length write ignored\n"); 307 return 0; 308 } 309 while (todo > 0) { 310 n = hdl->ops->write(hdl, data, todo); 311 if (n == 0) { 312 if (hdl->nbio || hdl->eof) 313 break; 314 if (!sio_psleep(hdl, POLLOUT)) 315 break; 316 continue; 317 } 318 data += n; 319 todo -= n; 320 #ifdef DEBUG 321 hdl->wcnt += n; 322 #endif 323 } 324 #ifdef DEBUG 325 if (sndio_debug >= 2) { 326 gettimeofday(&tv1, NULL); 327 timersub(&tv0, &hdl->tv, &dtv); 328 DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec); 329 330 timersub(&tv1, &tv0, &dtv); 331 us = dtv.tv_sec * 1000000 + dtv.tv_usec; 332 DPRINTF( 333 "sio_write: wrote %d bytes of %d in %uus\n", 334 (int)(len - todo), (int)len, us); 335 } 336 #endif 337 return len - todo; 338 } 339 340 int 341 sio_nfds(struct sio_hdl *hdl) 342 { 343 return hdl->ops->nfds(hdl); 344 } 345 346 int 347 sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events) 348 { 349 if (hdl->eof) 350 return 0; 351 if (!hdl->started) 352 events = 0; 353 return hdl->ops->pollfd(hdl, pfd, events); 354 } 355 356 int 357 sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) 358 { 359 int revents; 360 #ifdef DEBUG 361 struct timeval tv0, tv1, dtv; 362 unsigned us; 363 364 if (sndio_debug >= 2) 365 gettimeofday(&tv0, NULL); 366 #endif 367 if (hdl->eof) 368 return POLLHUP; 369 #ifdef DEBUG 370 hdl->pollcnt++; 371 #endif 372 revents = hdl->ops->revents(hdl, pfd); 373 if (!hdl->started) 374 return revents & POLLHUP; 375 #ifdef DEBUG 376 if (sndio_debug >= 2) { 377 gettimeofday(&tv1, NULL); 378 timersub(&tv0, &hdl->tv, &dtv); 379 DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec); 380 381 timersub(&tv1, &tv0, &dtv); 382 us = dtv.tv_sec * 1000000 + dtv.tv_usec; 383 DPRINTF("sio_revents: revents = 0x%x, complete in %uus\n", 384 revents, us); 385 } 386 #endif 387 return revents; 388 } 389 390 int 391 sio_eof(struct sio_hdl *hdl) 392 { 393 return hdl->eof; 394 } 395 396 void 397 sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) 398 { 399 if (hdl->started) { 400 DPRINTF("sio_onmove: already started\n"); 401 hdl->eof = 1; 402 return; 403 } 404 hdl->move_cb = cb; 405 hdl->move_addr = addr; 406 } 407 408 void 409 sio_onmove_cb(struct sio_hdl *hdl, int delta) 410 { 411 #ifdef DEBUG 412 struct timeval tv0, dtv; 413 long long playpos; 414 415 if (sndio_debug >= 3 && (hdl->mode & SIO_PLAY)) { 416 gettimeofday(&tv0, NULL); 417 timersub(&tv0, &hdl->tv, &dtv); 418 DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec); 419 hdl->realpos += delta; 420 playpos = hdl->wcnt / (hdl->par.bps * hdl->par.pchan); 421 DPRINTF("sio_onmove_cb: delta = %+7d, " 422 "plat = %+7lld, " 423 "realpos = %+7lld, " 424 "bufused = %+7lld\n", 425 delta, 426 playpos - hdl->realpos, 427 hdl->realpos, 428 hdl->realpos < 0 ? playpos : playpos - hdl->realpos); 429 } 430 #endif 431 if (hdl->move_cb) 432 hdl->move_cb(hdl->move_addr, delta); 433 } 434 435 int 436 sio_setvol(struct sio_hdl *hdl, unsigned ctl) 437 { 438 if (hdl->eof) 439 return 0; 440 if (!hdl->ops->setvol) 441 return 1; 442 if (!hdl->ops->setvol(hdl, ctl)) 443 return 0; 444 hdl->ops->getvol(hdl); 445 return 1; 446 } 447 448 int 449 sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned), void *addr) 450 { 451 if (hdl->started) { 452 DPRINTF("sio_onvol: already started\n"); 453 hdl->eof = 1; 454 return 0; 455 } 456 if (!hdl->ops->setvol) 457 return 0; 458 hdl->vol_cb = cb; 459 hdl->vol_addr = addr; 460 hdl->ops->getvol(hdl); 461 return 1; 462 } 463 464 void 465 sio_onvol_cb(struct sio_hdl *hdl, unsigned ctl) 466 { 467 if (hdl->vol_cb) 468 hdl->vol_cb(hdl->vol_addr, ctl); 469 } 470