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