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