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