1 /* 2 * QEMU OSS audio driver 3 * 4 * Copyright (c) 2003-2005 Vassili Karpov (malc) 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include <sys/ioctl.h> 27 #include <sys/soundcard.h> 28 #include "qemu/main-loop.h" 29 #include "qemu/module.h" 30 #include "qemu/host-utils.h" 31 #include "audio.h" 32 #include "trace.h" 33 34 #define AUDIO_CAP "oss" 35 #include "audio_int.h" 36 37 #if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY 38 #define USE_DSP_POLICY 39 #endif 40 41 typedef struct OSSVoiceOut { 42 HWVoiceOut hw; 43 int fd; 44 int nfrags; 45 int fragsize; 46 int mmapped; 47 Audiodev *dev; 48 } OSSVoiceOut; 49 50 typedef struct OSSVoiceIn { 51 HWVoiceIn hw; 52 int fd; 53 int nfrags; 54 int fragsize; 55 Audiodev *dev; 56 } OSSVoiceIn; 57 58 struct oss_params { 59 int freq; 60 int fmt; 61 int nchannels; 62 int nfrags; 63 int fragsize; 64 }; 65 66 static void G_GNUC_PRINTF (2, 3) oss_logerr (int err, const char *fmt, ...) 67 { 68 va_list ap; 69 70 va_start (ap, fmt); 71 AUD_vlog (AUDIO_CAP, fmt, ap); 72 va_end (ap); 73 74 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); 75 } 76 77 static void G_GNUC_PRINTF (3, 4) oss_logerr2 ( 78 int err, 79 const char *typ, 80 const char *fmt, 81 ... 82 ) 83 { 84 va_list ap; 85 86 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 87 88 va_start (ap, fmt); 89 AUD_vlog (AUDIO_CAP, fmt, ap); 90 va_end (ap); 91 92 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); 93 } 94 95 static void oss_anal_close (int *fdp) 96 { 97 int err; 98 99 qemu_set_fd_handler (*fdp, NULL, NULL, NULL); 100 err = close (*fdp); 101 if (err) { 102 oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); 103 } 104 *fdp = -1; 105 } 106 107 static void oss_helper_poll_out (void *opaque) 108 { 109 AudioState *s = opaque; 110 audio_run(s, "oss_poll_out"); 111 } 112 113 static void oss_helper_poll_in (void *opaque) 114 { 115 AudioState *s = opaque; 116 audio_run(s, "oss_poll_in"); 117 } 118 119 static void oss_poll_out (HWVoiceOut *hw) 120 { 121 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 122 123 qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s); 124 } 125 126 static void oss_poll_in (HWVoiceIn *hw) 127 { 128 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 129 130 qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s); 131 } 132 133 static int aud_to_ossfmt (AudioFormat fmt, int endianness) 134 { 135 switch (fmt) { 136 case AUDIO_FORMAT_S8: 137 return AFMT_S8; 138 139 case AUDIO_FORMAT_U8: 140 return AFMT_U8; 141 142 case AUDIO_FORMAT_S16: 143 if (endianness) { 144 return AFMT_S16_BE; 145 } else { 146 return AFMT_S16_LE; 147 } 148 149 case AUDIO_FORMAT_U16: 150 if (endianness) { 151 return AFMT_U16_BE; 152 } else { 153 return AFMT_U16_LE; 154 } 155 156 default: 157 dolog ("Internal logic error: Bad audio format %d\n", fmt); 158 #ifdef DEBUG_AUDIO 159 abort (); 160 #endif 161 return AFMT_U8; 162 } 163 } 164 165 static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) 166 { 167 switch (ossfmt) { 168 case AFMT_S8: 169 *endianness = 0; 170 *fmt = AUDIO_FORMAT_S8; 171 break; 172 173 case AFMT_U8: 174 *endianness = 0; 175 *fmt = AUDIO_FORMAT_U8; 176 break; 177 178 case AFMT_S16_LE: 179 *endianness = 0; 180 *fmt = AUDIO_FORMAT_S16; 181 break; 182 183 case AFMT_U16_LE: 184 *endianness = 0; 185 *fmt = AUDIO_FORMAT_U16; 186 break; 187 188 case AFMT_S16_BE: 189 *endianness = 1; 190 *fmt = AUDIO_FORMAT_S16; 191 break; 192 193 case AFMT_U16_BE: 194 *endianness = 1; 195 *fmt = AUDIO_FORMAT_U16; 196 break; 197 198 default: 199 dolog ("Unrecognized audio format %d\n", ossfmt); 200 return -1; 201 } 202 203 return 0; 204 } 205 206 #if defined DEBUG_MISMATCHES || defined DEBUG 207 static void oss_dump_info (struct oss_params *req, struct oss_params *obt) 208 { 209 dolog ("parameter | requested value | obtained value\n"); 210 dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); 211 dolog ("channels | %10d | %10d\n", 212 req->nchannels, obt->nchannels); 213 dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); 214 dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); 215 dolog ("fragsize | %10d | %10d\n", 216 req->fragsize, obt->fragsize); 217 } 218 #endif 219 220 #ifdef USE_DSP_POLICY 221 static int oss_get_version (int fd, int *version, const char *typ) 222 { 223 if (ioctl (fd, OSS_GETVERSION, &version)) { 224 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 225 /* 226 * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION 227 * since 7.x, but currently only on the mixer device (or in 228 * the Linuxolator), and in the native version that part of 229 * the code is in fact never reached so the ioctl fails anyway. 230 * Until this is fixed, just check the errno and if its what 231 * FreeBSD's sound drivers return atm assume they are new enough. 232 */ 233 if (errno == EINVAL) { 234 *version = 0x040000; 235 return 0; 236 } 237 #endif 238 oss_logerr2 (errno, typ, "Failed to get OSS version\n"); 239 return -1; 240 } 241 return 0; 242 } 243 #endif 244 245 static int oss_open(int in, struct oss_params *req, audsettings *as, 246 struct oss_params *obt, int *pfd, Audiodev *dev) 247 { 248 AudiodevOssOptions *oopts = &dev->u.oss; 249 AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out; 250 int fd; 251 int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; 252 audio_buf_info abinfo; 253 int fmt, freq, nchannels; 254 int setfragment = 1; 255 const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; 256 const char *typ = in ? "ADC" : "DAC"; 257 #ifdef USE_DSP_POLICY 258 int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5; 259 #endif 260 261 /* Kludge needed to have working mmap on Linux */ 262 oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? 263 O_RDWR : (in ? O_RDONLY : O_WRONLY); 264 265 fd = open (dspname, oflags | O_NONBLOCK); 266 if (-1 == fd) { 267 oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); 268 return -1; 269 } 270 271 freq = req->freq; 272 nchannels = req->nchannels; 273 fmt = req->fmt; 274 req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4; 275 req->fragsize = audio_buffer_bytes( 276 qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220); 277 278 if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { 279 oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); 280 goto err; 281 } 282 283 if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { 284 oss_logerr2 (errno, typ, "Failed to set number of channels %d\n", 285 req->nchannels); 286 goto err; 287 } 288 289 if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { 290 oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq); 291 goto err; 292 } 293 294 if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) { 295 oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); 296 goto err; 297 } 298 299 #ifdef USE_DSP_POLICY 300 if (policy >= 0) { 301 int version; 302 303 if (!oss_get_version (fd, &version, typ)) { 304 trace_oss_version(version); 305 306 if (version >= 0x040000) { 307 int policy2 = policy; 308 if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { 309 oss_logerr2 (errno, typ, 310 "Failed to set timing policy to %d\n", 311 policy); 312 goto err; 313 } 314 setfragment = 0; 315 } 316 } 317 } 318 #endif 319 320 if (setfragment) { 321 int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize); 322 if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { 323 oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", 324 req->nfrags, req->fragsize); 325 goto err; 326 } 327 } 328 329 if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { 330 oss_logerr2 (errno, typ, "Failed to get buffer length\n"); 331 goto err; 332 } 333 334 if (!abinfo.fragstotal || !abinfo.fragsize) { 335 AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n", 336 abinfo.fragstotal, abinfo.fragsize, typ); 337 goto err; 338 } 339 340 obt->fmt = fmt; 341 obt->nchannels = nchannels; 342 obt->freq = freq; 343 obt->nfrags = abinfo.fragstotal; 344 obt->fragsize = abinfo.fragsize; 345 *pfd = fd; 346 347 #ifdef DEBUG_MISMATCHES 348 if ((req->fmt != obt->fmt) || 349 (req->nchannels != obt->nchannels) || 350 (req->freq != obt->freq) || 351 (req->fragsize != obt->fragsize) || 352 (req->nfrags != obt->nfrags)) { 353 dolog ("Audio parameters mismatch\n"); 354 oss_dump_info (req, obt); 355 } 356 #endif 357 358 #ifdef DEBUG 359 oss_dump_info (req, obt); 360 #endif 361 return 0; 362 363 err: 364 oss_anal_close (&fd); 365 return -1; 366 } 367 368 static size_t oss_get_available_bytes(OSSVoiceOut *oss) 369 { 370 int err; 371 struct count_info cntinfo; 372 assert(oss->mmapped); 373 374 err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); 375 if (err < 0) { 376 oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n"); 377 return 0; 378 } 379 380 return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul); 381 } 382 383 static void oss_run_buffer_out(HWVoiceOut *hw) 384 { 385 OSSVoiceOut *oss = (OSSVoiceOut *)hw; 386 387 if (!oss->mmapped) { 388 audio_generic_run_buffer_out(hw); 389 } 390 } 391 392 static size_t oss_buffer_get_free(HWVoiceOut *hw) 393 { 394 OSSVoiceOut *oss = (OSSVoiceOut *)hw; 395 396 if (oss->mmapped) { 397 return oss_get_available_bytes(oss); 398 } else { 399 return audio_generic_buffer_get_free(hw); 400 } 401 } 402 403 static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size) 404 { 405 OSSVoiceOut *oss = (OSSVoiceOut *)hw; 406 407 if (oss->mmapped) { 408 *size = hw->size_emul - hw->pos_emul; 409 return hw->buf_emul + hw->pos_emul; 410 } else { 411 return audio_generic_get_buffer_out(hw, size); 412 } 413 } 414 415 static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) 416 { 417 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 418 if (oss->mmapped) { 419 assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul); 420 421 hw->pos_emul = (hw->pos_emul + size) % hw->size_emul; 422 return size; 423 } else { 424 return audio_generic_put_buffer_out(hw, buf, size); 425 } 426 } 427 428 static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len) 429 { 430 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 431 size_t pos; 432 433 if (oss->mmapped) { 434 size_t total_len; 435 len = MIN(len, oss_get_available_bytes(oss)); 436 437 total_len = len; 438 while (len) { 439 size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul); 440 memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy); 441 442 hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul; 443 buf += to_copy; 444 len -= to_copy; 445 } 446 return total_len; 447 } 448 449 pos = 0; 450 while (len) { 451 ssize_t bytes_written; 452 void *pcm = advance(buf, pos); 453 454 bytes_written = write(oss->fd, pcm, len); 455 if (bytes_written < 0) { 456 if (errno != EAGAIN) { 457 oss_logerr(errno, "failed to write %zu bytes\n", 458 len); 459 } 460 return pos; 461 } 462 463 pos += bytes_written; 464 if (bytes_written < len) { 465 break; 466 } 467 len -= bytes_written; 468 } 469 return pos; 470 } 471 472 static void oss_fini_out (HWVoiceOut *hw) 473 { 474 int err; 475 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 476 477 ldebug ("oss_fini\n"); 478 oss_anal_close (&oss->fd); 479 480 if (oss->mmapped && hw->buf_emul) { 481 err = munmap(hw->buf_emul, hw->size_emul); 482 if (err) { 483 oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n", 484 hw->buf_emul, hw->size_emul); 485 } 486 hw->buf_emul = NULL; 487 } 488 } 489 490 static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, 491 void *drv_opaque) 492 { 493 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 494 struct oss_params req, obt; 495 int endianness; 496 int err; 497 int fd; 498 AudioFormat effective_fmt; 499 struct audsettings obt_as; 500 Audiodev *dev = drv_opaque; 501 AudiodevOssOptions *oopts = &dev->u.oss; 502 503 oss->fd = -1; 504 505 req.fmt = aud_to_ossfmt (as->fmt, as->endianness); 506 req.freq = as->freq; 507 req.nchannels = as->nchannels; 508 509 if (oss_open(0, &req, as, &obt, &fd, dev)) { 510 return -1; 511 } 512 513 err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); 514 if (err) { 515 oss_anal_close (&fd); 516 return -1; 517 } 518 519 obt_as.freq = obt.freq; 520 obt_as.nchannels = obt.nchannels; 521 obt_as.fmt = effective_fmt; 522 obt_as.endianness = endianness; 523 524 audio_pcm_init_info (&hw->info, &obt_as); 525 oss->nfrags = obt.nfrags; 526 oss->fragsize = obt.fragsize; 527 528 if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) { 529 dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", 530 obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); 531 } 532 533 hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; 534 535 oss->mmapped = 0; 536 if (oopts->has_try_mmap && oopts->try_mmap) { 537 hw->size_emul = hw->samples * hw->info.bytes_per_frame; 538 hw->buf_emul = mmap( 539 NULL, 540 hw->size_emul, 541 PROT_READ | PROT_WRITE, 542 MAP_SHARED, 543 fd, 544 0 545 ); 546 if (hw->buf_emul == MAP_FAILED) { 547 oss_logerr(errno, "Failed to map %zu bytes of DAC\n", 548 hw->size_emul); 549 hw->buf_emul = NULL; 550 } else { 551 int err; 552 int trig = 0; 553 if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 554 oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); 555 } else { 556 trig = PCM_ENABLE_OUTPUT; 557 if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 558 oss_logerr ( 559 errno, 560 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" 561 ); 562 } else { 563 oss->mmapped = 1; 564 } 565 } 566 567 if (!oss->mmapped) { 568 err = munmap(hw->buf_emul, hw->size_emul); 569 if (err) { 570 oss_logerr(errno, "Failed to unmap buffer %p size %zu\n", 571 hw->buf_emul, hw->size_emul); 572 } 573 hw->buf_emul = NULL; 574 } 575 } 576 } 577 578 oss->fd = fd; 579 oss->dev = dev; 580 return 0; 581 } 582 583 static void oss_enable_out(HWVoiceOut *hw, bool enable) 584 { 585 int trig; 586 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 587 AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; 588 589 if (enable) { 590 hw->poll_mode = opdo->try_poll; 591 592 ldebug("enabling voice\n"); 593 if (hw->poll_mode) { 594 oss_poll_out(hw); 595 } 596 597 if (!oss->mmapped) { 598 return; 599 } 600 601 audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples); 602 trig = PCM_ENABLE_OUTPUT; 603 if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 604 oss_logerr(errno, 605 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"); 606 return; 607 } 608 } else { 609 if (hw->poll_mode) { 610 qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); 611 hw->poll_mode = 0; 612 } 613 614 if (!oss->mmapped) { 615 return; 616 } 617 618 ldebug ("disabling voice\n"); 619 trig = 0; 620 if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 621 oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); 622 return; 623 } 624 } 625 } 626 627 static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 628 { 629 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 630 struct oss_params req, obt; 631 int endianness; 632 int err; 633 int fd; 634 AudioFormat effective_fmt; 635 struct audsettings obt_as; 636 Audiodev *dev = drv_opaque; 637 638 oss->fd = -1; 639 640 req.fmt = aud_to_ossfmt (as->fmt, as->endianness); 641 req.freq = as->freq; 642 req.nchannels = as->nchannels; 643 if (oss_open(1, &req, as, &obt, &fd, dev)) { 644 return -1; 645 } 646 647 err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); 648 if (err) { 649 oss_anal_close (&fd); 650 return -1; 651 } 652 653 obt_as.freq = obt.freq; 654 obt_as.nchannels = obt.nchannels; 655 obt_as.fmt = effective_fmt; 656 obt_as.endianness = endianness; 657 658 audio_pcm_init_info (&hw->info, &obt_as); 659 oss->nfrags = obt.nfrags; 660 oss->fragsize = obt.fragsize; 661 662 if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) { 663 dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", 664 obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); 665 } 666 667 hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; 668 669 oss->fd = fd; 670 oss->dev = dev; 671 return 0; 672 } 673 674 static void oss_fini_in (HWVoiceIn *hw) 675 { 676 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 677 678 oss_anal_close (&oss->fd); 679 } 680 681 static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len) 682 { 683 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 684 size_t pos = 0; 685 686 while (len) { 687 ssize_t nread; 688 689 void *dst = advance(buf, pos); 690 nread = read(oss->fd, dst, len); 691 692 if (nread == -1) { 693 switch (errno) { 694 case EINTR: 695 case EAGAIN: 696 break; 697 default: 698 oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n", 699 len, dst); 700 break; 701 } 702 break; 703 } 704 705 pos += nread; 706 len -= nread; 707 } 708 709 return pos; 710 } 711 712 static void oss_enable_in(HWVoiceIn *hw, bool enable) 713 { 714 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 715 AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; 716 717 if (enable) { 718 hw->poll_mode = opdo->try_poll; 719 720 if (hw->poll_mode) { 721 oss_poll_in(hw); 722 } 723 } else { 724 if (hw->poll_mode) { 725 qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); 726 hw->poll_mode = 0; 727 } 728 } 729 } 730 731 static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) 732 { 733 if (!opdo->has_try_poll) { 734 opdo->try_poll = true; 735 opdo->has_try_poll = true; 736 } 737 } 738 739 static void *oss_audio_init(Audiodev *dev) 740 { 741 AudiodevOssOptions *oopts; 742 assert(dev->driver == AUDIODEV_DRIVER_OSS); 743 744 oopts = &dev->u.oss; 745 oss_init_per_direction(oopts->in); 746 oss_init_per_direction(oopts->out); 747 748 if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", 749 R_OK | W_OK) < 0 || 750 access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp", 751 R_OK | W_OK) < 0) { 752 return NULL; 753 } 754 return dev; 755 } 756 757 static void oss_audio_fini (void *opaque) 758 { 759 } 760 761 static struct audio_pcm_ops oss_pcm_ops = { 762 .init_out = oss_init_out, 763 .fini_out = oss_fini_out, 764 .write = oss_write, 765 .buffer_get_free = oss_buffer_get_free, 766 .run_buffer_out = oss_run_buffer_out, 767 .get_buffer_out = oss_get_buffer_out, 768 .put_buffer_out = oss_put_buffer_out, 769 .enable_out = oss_enable_out, 770 771 .init_in = oss_init_in, 772 .fini_in = oss_fini_in, 773 .read = oss_read, 774 .run_buffer_in = audio_generic_run_buffer_in, 775 .enable_in = oss_enable_in 776 }; 777 778 static struct audio_driver oss_audio_driver = { 779 .name = "oss", 780 .descr = "OSS http://www.opensound.com", 781 .init = oss_audio_init, 782 .fini = oss_audio_fini, 783 .pcm_ops = &oss_pcm_ops, 784 .can_be_default = 1, 785 .max_voices_out = INT_MAX, 786 .max_voices_in = INT_MAX, 787 .voice_size_out = sizeof (OSSVoiceOut), 788 .voice_size_in = sizeof (OSSVoiceIn) 789 }; 790 791 static void register_audio_oss(void) 792 { 793 audio_driver_register(&oss_audio_driver); 794 } 795 type_init(register_audio_oss); 796