1 /* $OpenBSD: sio_sun.c,v 1.11 2014/03/05 20:40:49 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 * TODO: 19 * 20 * remove filling code from sio_sun_write() and create sio_sun_fill() 21 * 22 * allow block size to be set 23 * 24 * call hdl->cb_pos() from sio_sun_read() and sio_sun_write(), or better: 25 * implement generic blocking sio_read() and sio_write() with poll(2) 26 * and use non-blocking sio_ops only 27 */ 28 29 #include <sys/types.h> 30 #include <sys/ioctl.h> 31 #include <sys/audioio.h> 32 #include <sys/stat.h> 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <limits.h> 37 #include <poll.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "debug.h" 44 #include "sio_priv.h" 45 46 struct sio_sun_hdl { 47 struct sio_hdl sio; 48 int fd; 49 int filling; 50 unsigned int ibpf, obpf; /* bytes per frame */ 51 unsigned int ibytes, obytes; /* bytes the hw transferred */ 52 unsigned int ierr, oerr; /* frames the hw dropped */ 53 int idelta, odelta; /* position reported to client */ 54 }; 55 56 static void sio_sun_close(struct sio_hdl *); 57 static int sio_sun_start(struct sio_hdl *); 58 static int sio_sun_stop(struct sio_hdl *); 59 static int sio_sun_setpar(struct sio_hdl *, struct sio_par *); 60 static int sio_sun_getpar(struct sio_hdl *, struct sio_par *); 61 static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *); 62 static size_t sio_sun_read(struct sio_hdl *, void *, size_t); 63 static size_t sio_sun_write(struct sio_hdl *, const void *, size_t); 64 static int sio_sun_nfds(struct sio_hdl *); 65 static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int); 66 static int sio_sun_revents(struct sio_hdl *, struct pollfd *); 67 68 static struct sio_ops sio_sun_ops = { 69 sio_sun_close, 70 sio_sun_setpar, 71 sio_sun_getpar, 72 sio_sun_getcap, 73 sio_sun_write, 74 sio_sun_read, 75 sio_sun_start, 76 sio_sun_stop, 77 sio_sun_nfds, 78 sio_sun_pollfd, 79 sio_sun_revents, 80 NULL, /* setvol */ 81 NULL, /* getvol */ 82 }; 83 84 /* 85 * convert sun encoding to sio_par encoding 86 */ 87 static int 88 sio_sun_infotoenc(struct sio_sun_hdl *hdl, struct audio_prinfo *ai, 89 struct sio_par *par) 90 { 91 par->msb = ai->msb; 92 par->bits = ai->precision; 93 par->bps = ai->bps; 94 switch (ai->encoding) { 95 case AUDIO_ENCODING_SLINEAR_LE: 96 par->le = 1; 97 par->sig = 1; 98 break; 99 case AUDIO_ENCODING_SLINEAR_BE: 100 par->le = 0; 101 par->sig = 1; 102 break; 103 case AUDIO_ENCODING_ULINEAR_LE: 104 par->le = 1; 105 par->sig = 0; 106 break; 107 case AUDIO_ENCODING_ULINEAR_BE: 108 par->le = 0; 109 par->sig = 0; 110 break; 111 case AUDIO_ENCODING_SLINEAR: 112 par->le = SIO_LE_NATIVE; 113 par->sig = 1; 114 break; 115 case AUDIO_ENCODING_ULINEAR: 116 par->le = SIO_LE_NATIVE; 117 par->sig = 0; 118 break; 119 default: 120 DPRINTF("sio_sun_infotoenc: unsupported encoding\n"); 121 hdl->sio.eof = 1; 122 return 0; 123 } 124 return 1; 125 } 126 127 /* 128 * convert sio_par encoding to sun encoding 129 */ 130 static void 131 sio_sun_enctoinfo(struct sio_sun_hdl *hdl, 132 unsigned int *renc, struct sio_par *par) 133 { 134 if (par->le == ~0U && par->sig == ~0U) { 135 *renc = ~0U; 136 } else if (par->le == ~0U || par->sig == ~0U) { 137 *renc = AUDIO_ENCODING_SLINEAR; 138 } else if (par->le && par->sig) { 139 *renc = AUDIO_ENCODING_SLINEAR_LE; 140 } else if (!par->le && par->sig) { 141 *renc = AUDIO_ENCODING_SLINEAR_BE; 142 } else if (par->le && !par->sig) { 143 *renc = AUDIO_ENCODING_ULINEAR_LE; 144 } else { 145 *renc = AUDIO_ENCODING_ULINEAR_BE; 146 } 147 } 148 149 /* 150 * try to set the device to the given parameters and check that the 151 * device can use them; return 1 on success, 0 on failure or error 152 */ 153 static int 154 sio_sun_tryinfo(struct sio_sun_hdl *hdl, struct sio_enc *enc, 155 unsigned int pchan, unsigned int rchan, unsigned int rate) 156 { 157 struct audio_info aui; 158 struct audio_prinfo *pr; 159 160 pr = (hdl->sio.mode & SIO_PLAY) ? &aui.play : &aui.record; 161 162 AUDIO_INITINFO(&aui); 163 if (enc) { 164 if (enc->le && enc->sig) { 165 pr->encoding = AUDIO_ENCODING_SLINEAR_LE; 166 } else if (!enc->le && enc->sig) { 167 pr->encoding = AUDIO_ENCODING_SLINEAR_BE; 168 } else if (enc->le && !enc->sig) { 169 pr->encoding = AUDIO_ENCODING_ULINEAR_LE; 170 } else { 171 pr->encoding = AUDIO_ENCODING_ULINEAR_BE; 172 } 173 pr->precision = enc->bits; 174 } 175 if (rate) 176 pr->sample_rate = rate; 177 if ((hdl->sio.mode & (SIO_PLAY | SIO_REC)) == (SIO_PLAY | SIO_REC)) 178 aui.record = aui.play; 179 if (pchan && (hdl->sio.mode & SIO_PLAY)) 180 aui.play.channels = pchan; 181 if (rchan && (hdl->sio.mode & SIO_REC)) 182 aui.record.channels = rchan; 183 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 184 if (errno == EINVAL) 185 return 0; 186 DPERROR("sio_sun_tryinfo: setinfo"); 187 hdl->sio.eof = 1; 188 return 0; 189 } 190 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 191 DPERROR("sio_sun_tryinfo: getinfo"); 192 hdl->sio.eof = 1; 193 return 0; 194 } 195 if (pchan && aui.play.channels != pchan) 196 return 0; 197 if (rchan && aui.record.channels != rchan) 198 return 0; 199 if (rate) { 200 if ((hdl->sio.mode & SIO_PLAY) && 201 (aui.play.sample_rate != rate)) 202 return 0; 203 if ((hdl->sio.mode & SIO_REC) && 204 (aui.record.sample_rate != rate)) 205 return 0; 206 } 207 return 1; 208 } 209 210 /* 211 * guess device capabilities 212 */ 213 static int 214 sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap) 215 { 216 #define NCHANS (sizeof(chans) / sizeof(chans[0])) 217 #define NRATES (sizeof(rates) / sizeof(rates[0])) 218 static unsigned int chans[] = { 219 1, 2, 4, 6, 8, 10, 12 220 }; 221 static unsigned int rates[] = { 222 8000, 11025, 12000, 16000, 22050, 24000, 223 32000, 44100, 48000, 64000, 88200, 96000 224 }; 225 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 226 struct sio_par savepar; 227 struct audio_encoding ae; 228 unsigned int nenc = 0, nconf = 0; 229 unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map; 230 unsigned int i, j, conf; 231 232 if (!sio_sun_getpar(&hdl->sio, &savepar)) 233 return 0; 234 235 /* 236 * fill encoding list 237 */ 238 for (ae.index = 0; nenc < SIO_NENC; ae.index++) { 239 if (ioctl(hdl->fd, AUDIO_GETENC, &ae) < 0) { 240 if (errno == EINVAL) 241 break; 242 DPERROR("sio_sun_getcap: getenc"); 243 hdl->sio.eof = 1; 244 return 0; 245 } 246 if (ae.flags & AUDIO_ENCODINGFLAG_EMULATED) 247 continue; 248 if (ae.encoding == AUDIO_ENCODING_SLINEAR_LE) { 249 cap->enc[nenc].le = 1; 250 cap->enc[nenc].sig = 1; 251 } else if (ae.encoding == AUDIO_ENCODING_SLINEAR_BE) { 252 cap->enc[nenc].le = 0; 253 cap->enc[nenc].sig = 1; 254 } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_LE) { 255 cap->enc[nenc].le = 1; 256 cap->enc[nenc].sig = 0; 257 } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_BE) { 258 cap->enc[nenc].le = 0; 259 cap->enc[nenc].sig = 0; 260 } else if (ae.encoding == AUDIO_ENCODING_SLINEAR) { 261 cap->enc[nenc].le = SIO_LE_NATIVE; 262 cap->enc[nenc].sig = 1; 263 } else if (ae.encoding == AUDIO_ENCODING_ULINEAR) { 264 cap->enc[nenc].le = SIO_LE_NATIVE; 265 cap->enc[nenc].sig = 0; 266 } else { 267 /* unsipported encoding */ 268 continue; 269 } 270 cap->enc[nenc].bits = ae.precision; 271 cap->enc[nenc].bps = ae.bps; 272 cap->enc[nenc].msb = ae.msb; 273 enc_map |= (1 << nenc); 274 nenc++; 275 } 276 277 /* 278 * fill channels 279 * 280 * for now we're lucky: all kernel devices assume that the 281 * number of channels and the encoding are independent so we can 282 * use the current encoding and try various channels. 283 */ 284 if (hdl->sio.mode & SIO_PLAY) { 285 memcpy(&cap->pchan, chans, NCHANS * sizeof(unsigned int)); 286 for (i = 0; i < NCHANS; i++) { 287 if (sio_sun_tryinfo(hdl, NULL, chans[i], 0, 0)) 288 pchan_map |= (1 << i); 289 } 290 } 291 if (hdl->sio.mode & SIO_REC) { 292 memcpy(&cap->rchan, chans, NCHANS * sizeof(unsigned int)); 293 for (i = 0; i < NCHANS; i++) { 294 if (sio_sun_tryinfo(hdl, NULL, 0, chans[i], 0)) 295 rchan_map |= (1 << i); 296 } 297 } 298 299 /* 300 * fill rates 301 * 302 * rates are not independent from other parameters (eg. on 303 * uaudio devices), so certain rates may not be allowed with 304 * certain encodings. We have to check rates for all encodings 305 */ 306 memcpy(&cap->rate, rates, NRATES * sizeof(unsigned int)); 307 for (j = 0; j < nenc; j++) { 308 rate_map = 0; 309 for (i = 0; i < NRATES; i++) { 310 if (sio_sun_tryinfo(hdl, &cap->enc[j], 0, 0, rates[i])) 311 rate_map |= (1 << i); 312 } 313 for (conf = 0; conf < nconf; conf++) { 314 if (cap->confs[conf].rate == rate_map) { 315 cap->confs[conf].enc |= (1 << j); 316 break; 317 } 318 } 319 if (conf == nconf) { 320 if (nconf == SIO_NCONF) 321 break; 322 cap->confs[nconf].enc = (1 << j); 323 cap->confs[nconf].pchan = pchan_map; 324 cap->confs[nconf].rchan = rchan_map; 325 cap->confs[nconf].rate = rate_map; 326 nconf++; 327 } 328 } 329 cap->nconf = nconf; 330 if (!sio_sun_setpar(&hdl->sio, &savepar)) 331 return 0; 332 return 1; 333 #undef NCHANS 334 #undef NRATES 335 } 336 337 struct sio_hdl * 338 _sio_sun_open(const char *str, unsigned int mode, int nbio) 339 { 340 int fd, flags, fullduplex; 341 struct audio_info aui; 342 struct sio_sun_hdl *hdl; 343 struct sio_par par; 344 char path[PATH_MAX]; 345 346 switch (*str) { 347 case '/': 348 case ':': /* XXX: for backward compat */ 349 str++; 350 break; 351 default: 352 DPRINTF("_sio_sun_open: %s: '/<devnum>' expected\n", str); 353 return NULL; 354 } 355 hdl = malloc(sizeof(struct sio_sun_hdl)); 356 if (hdl == NULL) 357 return NULL; 358 _sio_create(&hdl->sio, &sio_sun_ops, mode, nbio); 359 360 snprintf(path, sizeof(path), "/dev/audio%s", str); 361 if (mode == (SIO_PLAY | SIO_REC)) 362 flags = O_RDWR; 363 else 364 flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY; 365 366 while ((fd = open(path, flags | O_NONBLOCK)) < 0) { 367 if (errno == EINTR) 368 continue; 369 DPERROR(path); 370 goto bad_free; 371 } 372 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { 373 DPERROR("FD_CLOEXEC"); 374 goto bad_close; 375 } 376 377 /* 378 * pause the device 379 */ 380 AUDIO_INITINFO(&aui); 381 if (mode & SIO_PLAY) 382 aui.play.pause = 1; 383 if (mode & SIO_REC) 384 aui.record.pause = 1; 385 if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) { 386 DPERROR("sio_open_sun: setinfo"); 387 goto bad_close; 388 } 389 /* 390 * If both play and record are requested then 391 * set full duplex mode. 392 */ 393 if (mode == (SIO_PLAY | SIO_REC)) { 394 fullduplex = 1; 395 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) { 396 DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path); 397 goto bad_close; 398 } 399 } 400 hdl->fd = fd; 401 402 /* 403 * Default parameters may not be compatible with libsndio (eg. mulaw 404 * encodings, different playback and recording parameters, etc...), so 405 * set parameters to a random value. If the requested parameters are 406 * not supported by the device, then sio_setpar() will pick supported 407 * ones. 408 */ 409 sio_initpar(&par); 410 par.rate = 48000; 411 par.le = SIO_LE_NATIVE; 412 par.sig = 1; 413 par.bits = 16; 414 par.appbufsz = 1200; 415 if (!sio_setpar(&hdl->sio, &par)) 416 goto bad_close; 417 return (struct sio_hdl *)hdl; 418 bad_close: 419 while (close(fd) < 0 && errno == EINTR) 420 ; /* retry */ 421 bad_free: 422 free(hdl); 423 return NULL; 424 } 425 426 static void 427 sio_sun_close(struct sio_hdl *sh) 428 { 429 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 430 431 while (close(hdl->fd) < 0 && errno == EINTR) 432 ; /* retry */ 433 free(hdl); 434 } 435 436 static int 437 sio_sun_start(struct sio_hdl *sh) 438 { 439 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 440 struct audio_info aui; 441 442 hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps; 443 hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps; 444 hdl->ibytes = 0; 445 hdl->obytes = 0; 446 hdl->ierr = 0; 447 hdl->oerr = 0; 448 hdl->idelta = 0; 449 hdl->odelta = 0; 450 451 if (hdl->sio.mode & SIO_PLAY) { 452 /* 453 * keep the device paused and let sio_sun_write() trigger the 454 * start later, to avoid buffer underruns 455 */ 456 hdl->filling = 1; 457 } else { 458 /* 459 * no play buffers to fill, start now! 460 */ 461 AUDIO_INITINFO(&aui); 462 if (hdl->sio.mode & SIO_REC) 463 aui.record.pause = 0; 464 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 465 DPERROR("sio_sun_start: setinfo"); 466 hdl->sio.eof = 1; 467 return 0; 468 } 469 hdl->filling = 0; 470 _sio_onmove_cb(&hdl->sio, 0); 471 } 472 return 1; 473 } 474 475 static int 476 sio_sun_stop(struct sio_hdl *sh) 477 { 478 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 479 struct audio_info aui; 480 int mode; 481 482 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 483 DPERROR("sio_sun_stop: getinfo"); 484 hdl->sio.eof = 1; 485 return 0; 486 } 487 mode = aui.mode; 488 489 /* 490 * there's no way to drain the device without blocking, so just 491 * stop it until the kernel driver get fixed 492 */ 493 AUDIO_INITINFO(&aui); 494 aui.mode = 0; 495 if (hdl->sio.mode & SIO_PLAY) 496 aui.play.pause = 1; 497 if (hdl->sio.mode & SIO_REC) 498 aui.record.pause = 1; 499 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 500 DPERROR("sio_sun_stop: setinfo1"); 501 hdl->sio.eof = 1; 502 return 0; 503 } 504 AUDIO_INITINFO(&aui); 505 aui.mode = mode; 506 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 507 DPERROR("sio_sun_stop: setinfo2"); 508 hdl->sio.eof = 1; 509 return 0; 510 } 511 return 1; 512 } 513 514 static int 515 sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par) 516 { 517 #define NRETRIES 8 518 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 519 struct audio_info aui; 520 unsigned int i, infr, ibpf, onfr, obpf; 521 unsigned int bufsz, round; 522 unsigned int rate, req_rate, prec, enc; 523 524 /* 525 * try to set parameters until the device accepts 526 * a common encoding and rate for play and record 527 */ 528 rate = par->rate; 529 prec = par->bits; 530 sio_sun_enctoinfo(hdl, &enc, par); 531 for (i = 0;; i++) { 532 if (i == NRETRIES) { 533 DPRINTF("sio_sun_setpar: couldn't set parameters\n"); 534 hdl->sio.eof = 1; 535 return 0; 536 } 537 AUDIO_INITINFO(&aui); 538 if (hdl->sio.mode & SIO_PLAY) { 539 aui.play.sample_rate = rate; 540 aui.play.precision = prec; 541 aui.play.encoding = enc; 542 aui.play.channels = par->pchan; 543 } 544 if (hdl->sio.mode & SIO_REC) { 545 aui.record.sample_rate = rate; 546 aui.record.precision = prec; 547 aui.record.encoding = enc; 548 aui.record.channels = par->rchan; 549 } 550 DPRINTFN(2, "sio_sun_setpar: %i: trying pars = %u/%u/%u\n", 551 i, rate, prec, enc); 552 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) { 553 DPERROR("sio_sun_setpar: setinfo(pars)"); 554 hdl->sio.eof = 1; 555 return 0; 556 } 557 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 558 DPERROR("sio_sun_setpar: getinfo(pars)"); 559 hdl->sio.eof = 1; 560 return 0; 561 } 562 enc = (hdl->sio.mode & SIO_REC) ? 563 aui.record.encoding : aui.play.encoding; 564 switch (enc) { 565 case AUDIO_ENCODING_SLINEAR_LE: 566 case AUDIO_ENCODING_SLINEAR_BE: 567 case AUDIO_ENCODING_ULINEAR_LE: 568 case AUDIO_ENCODING_ULINEAR_BE: 569 case AUDIO_ENCODING_SLINEAR: 570 case AUDIO_ENCODING_ULINEAR: 571 break; 572 default: 573 DPRINTF("sio_sun_setpar: couldn't set linear encoding\n"); 574 hdl->sio.eof = 1; 575 return 0; 576 } 577 if (hdl->sio.mode != (SIO_REC | SIO_PLAY)) 578 break; 579 if (aui.play.sample_rate == aui.record.sample_rate && 580 aui.play.precision == aui.record.precision && 581 aui.play.encoding == aui.record.encoding) 582 break; 583 if (i < NRETRIES / 2) { 584 rate = aui.play.sample_rate; 585 prec = aui.play.precision; 586 enc = aui.play.encoding; 587 } else { 588 rate = aui.record.sample_rate; 589 prec = aui.record.precision; 590 enc = aui.record.encoding; 591 } 592 } 593 594 /* 595 * If the rate that the hardware is using is different than 596 * the requested rate, scale buffer sizes so they will be the 597 * same time duration as what was requested. This just gets 598 * the rates to use for scaling, that actual scaling is done 599 * later. 600 */ 601 rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate : 602 aui.play.sample_rate; 603 req_rate = rate; 604 if (par->rate && par->rate != ~0U) 605 req_rate = par->rate; 606 607 /* 608 * if block size and buffer size are not both set then 609 * set the blocksize to half the buffer size 610 */ 611 bufsz = par->appbufsz; 612 round = par->round; 613 if (bufsz != ~0U) { 614 bufsz = bufsz * rate / req_rate; 615 if (round == ~0U) 616 round = (bufsz + 1) / 2; 617 else 618 round = round * rate / req_rate; 619 } else if (round != ~0U) { 620 round = round * rate / req_rate; 621 bufsz = round * 2; 622 } else 623 return 1; 624 625 /* 626 * get the play/record frame size in bytes 627 */ 628 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 629 DPERROR("sio_sun_setpar: GETINFO"); 630 hdl->sio.eof = 1; 631 return 0; 632 } 633 ibpf = (hdl->sio.mode & SIO_REC) ? 634 aui.record.channels * aui.record.bps : 1; 635 obpf = (hdl->sio.mode & SIO_PLAY) ? 636 aui.play.channels * aui.play.bps : 1; 637 638 DPRINTFN(2, "sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf); 639 640 /* 641 * try to set parameters until the device accepts 642 * a common block size for play and record 643 */ 644 for (i = 0; i < NRETRIES; i++) { 645 AUDIO_INITINFO(&aui); 646 aui.hiwat = (bufsz + round - 1) / round; 647 aui.lowat = aui.hiwat; 648 if (hdl->sio.mode & SIO_REC) 649 aui.record.block_size = round * ibpf; 650 if (hdl->sio.mode & SIO_PLAY) 651 aui.play.block_size = round * obpf; 652 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 653 DPERROR("sio_sun_setpar2: SETINFO"); 654 hdl->sio.eof = 1; 655 return 0; 656 } 657 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 658 DPERROR("sio_sun_setpar2: GETINFO"); 659 hdl->sio.eof = 1; 660 return 0; 661 } 662 infr = aui.record.block_size / ibpf; 663 onfr = aui.play.block_size / obpf; 664 DPRINTFN(2, "sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n", 665 i, round, infr, onfr); 666 667 /* 668 * if half-duplex or both block sizes match, we're done 669 */ 670 if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) { 671 DPRINTFN(2, "sio_sun_setpar: blocksize ok\n"); 672 return 1; 673 } 674 675 /* 676 * half of the retries, retry with the smaller value, 677 * then with the larger returned value 678 */ 679 if (i < NRETRIES / 2) 680 round = infr < onfr ? infr : onfr; 681 else 682 round = infr < onfr ? onfr : infr; 683 } 684 DPRINTFN(2, "sio_sun_setpar: couldn't find a working blocksize\n"); 685 hdl->sio.eof = 1; 686 return 0; 687 #undef NRETRIES 688 } 689 690 static int 691 sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par) 692 { 693 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 694 struct audio_info aui; 695 696 if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 697 DPERROR("sio_sun_getpar: getinfo"); 698 hdl->sio.eof = 1; 699 return 0; 700 } 701 if (hdl->sio.mode & SIO_PLAY) { 702 par->rate = aui.play.sample_rate; 703 if (!sio_sun_infotoenc(hdl, &aui.play, par)) 704 return 0; 705 } else if (hdl->sio.mode & SIO_REC) { 706 par->rate = aui.record.sample_rate; 707 if (!sio_sun_infotoenc(hdl, &aui.record, par)) 708 return 0; 709 } else 710 return 0; 711 par->pchan = (hdl->sio.mode & SIO_PLAY) ? 712 aui.play.channels : 0; 713 par->rchan = (hdl->sio.mode & SIO_REC) ? 714 aui.record.channels : 0; 715 par->round = (hdl->sio.mode & SIO_REC) ? 716 aui.record.block_size / (par->bps * par->rchan) : 717 aui.play.block_size / (par->bps * par->pchan); 718 par->appbufsz = aui.hiwat * par->round; 719 par->bufsz = par->appbufsz; 720 return 1; 721 } 722 723 static size_t 724 sio_sun_read(struct sio_hdl *sh, void *buf, size_t len) 725 { 726 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 727 ssize_t n; 728 729 while ((n = read(hdl->fd, buf, len)) < 0) { 730 if (errno == EINTR) 731 continue; 732 if (errno != EAGAIN) { 733 DPERROR("sio_sun_read: read"); 734 hdl->sio.eof = 1; 735 } 736 return 0; 737 } 738 if (n == 0) { 739 DPRINTF("sio_sun_read: eof\n"); 740 hdl->sio.eof = 1; 741 return 0; 742 } 743 return n; 744 } 745 746 static size_t 747 sio_sun_autostart(struct sio_sun_hdl *hdl) 748 { 749 struct audio_info aui; 750 struct pollfd pfd; 751 752 pfd.fd = hdl->fd; 753 pfd.events = POLLOUT; 754 while (poll(&pfd, 1, 0) < 0) { 755 if (errno == EINTR) 756 continue; 757 DPERROR("sio_sun_autostart: poll"); 758 hdl->sio.eof = 1; 759 return 0; 760 } 761 if (!(pfd.revents & POLLOUT)) { 762 hdl->filling = 0; 763 AUDIO_INITINFO(&aui); 764 if (hdl->sio.mode & SIO_PLAY) 765 aui.play.pause = 0; 766 if (hdl->sio.mode & SIO_REC) 767 aui.record.pause = 0; 768 if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 769 DPERROR("sio_sun_autostart: setinfo"); 770 hdl->sio.eof = 1; 771 return 0; 772 } 773 _sio_onmove_cb(&hdl->sio, 0); 774 } 775 return 1; 776 } 777 778 static size_t 779 sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) 780 { 781 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 782 const unsigned char *data = buf; 783 ssize_t n, todo; 784 785 todo = len; 786 while ((n = write(hdl->fd, data, todo)) < 0) { 787 if (errno == EINTR) 788 continue; 789 if (errno != EAGAIN) { 790 DPERROR("sio_sun_write: write"); 791 hdl->sio.eof = 1; 792 } 793 return 0; 794 } 795 if (hdl->filling) { 796 if (!sio_sun_autostart(hdl)) 797 return 0; 798 } 799 return n; 800 } 801 802 static int 803 sio_sun_nfds(struct sio_hdl *hdl) 804 { 805 return 1; 806 } 807 808 static int 809 sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events) 810 { 811 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 812 813 pfd->fd = hdl->fd; 814 pfd->events = events; 815 return 1; 816 } 817 818 int 819 sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) 820 { 821 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 822 struct audio_offset ao; 823 int xrun, dierr = 0, doerr = 0, offset, delta; 824 int revents = pfd->revents; 825 826 if (!hdl->sio.started) 827 return pfd->revents; 828 if (hdl->sio.mode & SIO_PLAY) { 829 if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) { 830 DPERROR("sio_sun_revents: GETOOFFS"); 831 hdl->sio.eof = 1; 832 return POLLHUP; 833 } 834 delta = (ao.samples - hdl->obytes) / hdl->obpf; 835 hdl->obytes = ao.samples; 836 hdl->odelta += delta; 837 if (!(hdl->sio.mode & SIO_REC)) 838 hdl->idelta += delta; 839 } 840 if (hdl->sio.mode & SIO_REC) { 841 if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) { 842 DPERROR("sio_sun_revents: GETIOFFS"); 843 hdl->sio.eof = 1; 844 return POLLHUP; 845 } 846 delta = (ao.samples - hdl->ibytes) / hdl->ibpf; 847 hdl->ibytes = ao.samples; 848 hdl->idelta += delta; 849 if (!(hdl->sio.mode & SIO_PLAY)) 850 hdl->odelta += delta; 851 } 852 if (hdl->sio.mode & SIO_PLAY) { 853 if (ioctl(hdl->fd, AUDIO_PERROR, &xrun) < 0) { 854 DPERROR("sio_sun_revents: PERROR"); 855 hdl->sio.eof = 1; 856 return POLLHUP; 857 } 858 doerr = xrun - hdl->oerr; 859 hdl->oerr = xrun; 860 if (!(hdl->sio.mode & SIO_REC)) 861 dierr = doerr; 862 if (doerr > 0) 863 DPRINTFN(2, "play xrun %d\n", doerr); 864 } 865 if (hdl->sio.mode & SIO_REC) { 866 if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) { 867 DPERROR("sio_sun_revents: RERROR"); 868 hdl->sio.eof = 1; 869 return POLLHUP; 870 } 871 dierr = xrun - hdl->ierr; 872 hdl->ierr = xrun; 873 if (!(hdl->sio.mode & SIO_PLAY)) 874 doerr = dierr; 875 if (dierr > 0) 876 DPRINTFN(2, "rec xrun %d\n", dierr); 877 } 878 879 /* 880 * GET{I,O}OFFS report positions including xruns, 881 * so we have to substract to get the real position 882 */ 883 hdl->idelta -= dierr; 884 hdl->odelta -= doerr; 885 886 offset = doerr - dierr; 887 if (offset > 0) { 888 hdl->sio.rdrop += offset * hdl->ibpf; 889 hdl->idelta -= offset; 890 DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr); 891 } else if (offset < 0) { 892 hdl->sio.wsil += -offset * hdl->obpf; 893 hdl->odelta -= offset; 894 DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr); 895 } 896 897 delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta; 898 if (delta > 0) { 899 _sio_onmove_cb(&hdl->sio, delta); 900 hdl->idelta -= delta; 901 hdl->odelta -= delta; 902 } 903 904 if (hdl->filling) 905 revents |= POLLOUT; /* XXX: is this necessary ? */ 906 return revents; 907 } 908