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