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