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