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 #include <stdlib.h> 25 #include <sys/mman.h> 26 #include <sys/types.h> 27 #include <sys/ioctl.h> 28 #ifdef __OpenBSD__ 29 #include <soundcard.h> 30 #else 31 #include <sys/soundcard.h> 32 #endif 33 #include "qemu-common.h" 34 #include "audio.h" 35 36 #define AUDIO_CAP "oss" 37 #include "audio_int.h" 38 39 typedef struct OSSVoiceOut { 40 HWVoiceOut hw; 41 void *pcm_buf; 42 int fd; 43 int nfrags; 44 int fragsize; 45 int mmapped; 46 int old_optr; 47 } OSSVoiceOut; 48 49 typedef struct OSSVoiceIn { 50 HWVoiceIn hw; 51 void *pcm_buf; 52 int fd; 53 int nfrags; 54 int fragsize; 55 int old_optr; 56 } OSSVoiceIn; 57 58 static struct { 59 int try_mmap; 60 int nfrags; 61 int fragsize; 62 const char *devpath_out; 63 const char *devpath_in; 64 int debug; 65 } conf = { 66 .try_mmap = 0, 67 .nfrags = 4, 68 .fragsize = 4096, 69 .devpath_out = "/dev/dsp", 70 .devpath_in = "/dev/dsp", 71 .debug = 0 72 }; 73 74 struct oss_params { 75 int freq; 76 audfmt_e fmt; 77 int nchannels; 78 int nfrags; 79 int fragsize; 80 }; 81 82 static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) 83 { 84 va_list ap; 85 86 va_start (ap, fmt); 87 AUD_vlog (AUDIO_CAP, fmt, ap); 88 va_end (ap); 89 90 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); 91 } 92 93 static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( 94 int err, 95 const char *typ, 96 const char *fmt, 97 ... 98 ) 99 { 100 va_list ap; 101 102 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 103 104 va_start (ap, fmt); 105 AUD_vlog (AUDIO_CAP, fmt, ap); 106 va_end (ap); 107 108 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); 109 } 110 111 static void oss_anal_close (int *fdp) 112 { 113 int err = close (*fdp); 114 if (err) { 115 oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); 116 } 117 *fdp = -1; 118 } 119 120 static int oss_write (SWVoiceOut *sw, void *buf, int len) 121 { 122 return audio_pcm_sw_write (sw, buf, len); 123 } 124 125 static int aud_to_ossfmt (audfmt_e fmt) 126 { 127 switch (fmt) { 128 case AUD_FMT_S8: 129 return AFMT_S8; 130 131 case AUD_FMT_U8: 132 return AFMT_U8; 133 134 case AUD_FMT_S16: 135 return AFMT_S16_LE; 136 137 case AUD_FMT_U16: 138 return AFMT_U16_LE; 139 140 default: 141 dolog ("Internal logic error: Bad audio format %d\n", fmt); 142 #ifdef DEBUG_AUDIO 143 abort (); 144 #endif 145 return AFMT_U8; 146 } 147 } 148 149 static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) 150 { 151 switch (ossfmt) { 152 case AFMT_S8: 153 *endianness = 0; 154 *fmt = AUD_FMT_S8; 155 break; 156 157 case AFMT_U8: 158 *endianness = 0; 159 *fmt = AUD_FMT_U8; 160 break; 161 162 case AFMT_S16_LE: 163 *endianness = 0; 164 *fmt = AUD_FMT_S16; 165 break; 166 167 case AFMT_U16_LE: 168 *endianness = 0; 169 *fmt = AUD_FMT_U16; 170 break; 171 172 case AFMT_S16_BE: 173 *endianness = 1; 174 *fmt = AUD_FMT_S16; 175 break; 176 177 case AFMT_U16_BE: 178 *endianness = 1; 179 *fmt = AUD_FMT_U16; 180 break; 181 182 default: 183 dolog ("Unrecognized audio format %d\n", ossfmt); 184 return -1; 185 } 186 187 return 0; 188 } 189 190 #if defined DEBUG_MISMATCHES || defined DEBUG 191 static void oss_dump_info (struct oss_params *req, struct oss_params *obt) 192 { 193 dolog ("parameter | requested value | obtained value\n"); 194 dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); 195 dolog ("channels | %10d | %10d\n", 196 req->nchannels, obt->nchannels); 197 dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); 198 dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); 199 dolog ("fragsize | %10d | %10d\n", 200 req->fragsize, obt->fragsize); 201 } 202 #endif 203 204 static int oss_open (int in, struct oss_params *req, 205 struct oss_params *obt, int *pfd) 206 { 207 int fd; 208 int mmmmssss; 209 audio_buf_info abinfo; 210 int fmt, freq, nchannels; 211 const char *dspname = in ? conf.devpath_in : conf.devpath_out; 212 const char *typ = in ? "ADC" : "DAC"; 213 214 fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK); 215 if (-1 == fd) { 216 oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); 217 return -1; 218 } 219 220 freq = req->freq; 221 nchannels = req->nchannels; 222 fmt = req->fmt; 223 224 if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { 225 oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); 226 goto err; 227 } 228 229 if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { 230 oss_logerr2 (errno, typ, "Failed to set number of channels %d\n", 231 req->nchannels); 232 goto err; 233 } 234 235 if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { 236 oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq); 237 goto err; 238 } 239 240 if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) { 241 oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); 242 goto err; 243 } 244 245 mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); 246 if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { 247 oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", 248 req->nfrags, req->fragsize); 249 goto err; 250 } 251 252 if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { 253 oss_logerr2 (errno, typ, "Failed to get buffer length\n"); 254 goto err; 255 } 256 257 if (!abinfo.fragstotal || !abinfo.fragsize) { 258 AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n", 259 abinfo.fragstotal, abinfo.fragsize, typ); 260 goto err; 261 } 262 263 obt->fmt = fmt; 264 obt->nchannels = nchannels; 265 obt->freq = freq; 266 obt->nfrags = abinfo.fragstotal; 267 obt->fragsize = abinfo.fragsize; 268 *pfd = fd; 269 270 #ifdef DEBUG_MISMATCHES 271 if ((req->fmt != obt->fmt) || 272 (req->nchannels != obt->nchannels) || 273 (req->freq != obt->freq) || 274 (req->fragsize != obt->fragsize) || 275 (req->nfrags != obt->nfrags)) { 276 dolog ("Audio parameters mismatch\n"); 277 oss_dump_info (req, obt); 278 } 279 #endif 280 281 #ifdef DEBUG 282 oss_dump_info (req, obt); 283 #endif 284 return 0; 285 286 err: 287 oss_anal_close (&fd); 288 return -1; 289 } 290 291 static int oss_run_out (HWVoiceOut *hw) 292 { 293 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 294 int err, rpos, live, decr; 295 int samples; 296 uint8_t *dst; 297 struct st_sample *src; 298 struct audio_buf_info abinfo; 299 struct count_info cntinfo; 300 int bufsize; 301 302 live = audio_pcm_hw_get_live_out (hw); 303 if (!live) { 304 return 0; 305 } 306 307 bufsize = hw->samples << hw->info.shift; 308 309 if (oss->mmapped) { 310 int bytes; 311 312 err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); 313 if (err < 0) { 314 oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); 315 return 0; 316 } 317 318 if (cntinfo.ptr == oss->old_optr) { 319 if (abs (hw->samples - live) < 64) { 320 dolog ("warning: Overrun\n"); 321 } 322 return 0; 323 } 324 325 if (cntinfo.ptr > oss->old_optr) { 326 bytes = cntinfo.ptr - oss->old_optr; 327 } 328 else { 329 bytes = bufsize + cntinfo.ptr - oss->old_optr; 330 } 331 332 decr = audio_MIN (bytes >> hw->info.shift, live); 333 } 334 else { 335 err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); 336 if (err < 0) { 337 oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); 338 return 0; 339 } 340 341 if (abinfo.bytes > bufsize) { 342 if (conf.debug) { 343 dolog ("warning: Invalid available size, size=%d bufsize=%d\n" 344 "please report your OS/audio hw to malc (at) pulsesoft.com\n", 345 abinfo.bytes, bufsize); 346 } 347 abinfo.bytes = bufsize; 348 } 349 350 if (abinfo.bytes < 0) { 351 if (conf.debug) { 352 dolog ("warning: Invalid available size, size=%d bufsize=%d\n", 353 abinfo.bytes, bufsize); 354 } 355 return 0; 356 } 357 358 decr = audio_MIN (abinfo.bytes >> hw->info.shift, live); 359 if (!decr) { 360 return 0; 361 } 362 } 363 364 samples = decr; 365 rpos = hw->rpos; 366 while (samples) { 367 int left_till_end_samples = hw->samples - rpos; 368 int convert_samples = audio_MIN (samples, left_till_end_samples); 369 370 src = hw->mix_buf + rpos; 371 dst = advance (oss->pcm_buf, rpos << hw->info.shift); 372 373 hw->clip (dst, src, convert_samples); 374 if (!oss->mmapped) { 375 int written; 376 377 written = write (oss->fd, dst, convert_samples << hw->info.shift); 378 /* XXX: follow errno recommendations ? */ 379 if (written == -1) { 380 oss_logerr ( 381 errno, 382 "Failed to write %d bytes of audio data from %p\n", 383 convert_samples << hw->info.shift, 384 dst 385 ); 386 continue; 387 } 388 389 if (written != convert_samples << hw->info.shift) { 390 int wsamples = written >> hw->info.shift; 391 int wbytes = wsamples << hw->info.shift; 392 if (wbytes != written) { 393 dolog ("warning: Misaligned write %d (requested %d), " 394 "alignment %d\n", 395 wbytes, written, hw->info.align + 1); 396 } 397 decr -= wsamples; 398 rpos = (rpos + wsamples) % hw->samples; 399 break; 400 } 401 } 402 403 rpos = (rpos + convert_samples) % hw->samples; 404 samples -= convert_samples; 405 } 406 if (oss->mmapped) { 407 oss->old_optr = cntinfo.ptr; 408 } 409 410 hw->rpos = rpos; 411 return decr; 412 } 413 414 static void oss_fini_out (HWVoiceOut *hw) 415 { 416 int err; 417 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 418 419 ldebug ("oss_fini\n"); 420 oss_anal_close (&oss->fd); 421 422 if (oss->pcm_buf) { 423 if (oss->mmapped) { 424 err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); 425 if (err) { 426 oss_logerr (errno, "Failed to unmap buffer %p, size %d\n", 427 oss->pcm_buf, hw->samples << hw->info.shift); 428 } 429 } 430 else { 431 qemu_free (oss->pcm_buf); 432 } 433 oss->pcm_buf = NULL; 434 } 435 } 436 437 static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) 438 { 439 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 440 struct oss_params req, obt; 441 int endianness; 442 int err; 443 int fd; 444 audfmt_e effective_fmt; 445 struct audsettings obt_as; 446 447 oss->fd = -1; 448 449 req.fmt = aud_to_ossfmt (as->fmt); 450 req.freq = as->freq; 451 req.nchannels = as->nchannels; 452 req.fragsize = conf.fragsize; 453 req.nfrags = conf.nfrags; 454 455 if (oss_open (0, &req, &obt, &fd)) { 456 return -1; 457 } 458 459 err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); 460 if (err) { 461 oss_anal_close (&fd); 462 return -1; 463 } 464 465 obt_as.freq = obt.freq; 466 obt_as.nchannels = obt.nchannels; 467 obt_as.fmt = effective_fmt; 468 obt_as.endianness = endianness; 469 470 audio_pcm_init_info (&hw->info, &obt_as); 471 oss->nfrags = obt.nfrags; 472 oss->fragsize = obt.fragsize; 473 474 if (obt.nfrags * obt.fragsize & hw->info.align) { 475 dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", 476 obt.nfrags * obt.fragsize, hw->info.align + 1); 477 } 478 479 hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; 480 481 oss->mmapped = 0; 482 if (conf.try_mmap) { 483 oss->pcm_buf = mmap ( 484 0, 485 hw->samples << hw->info.shift, 486 PROT_READ | PROT_WRITE, 487 MAP_SHARED, 488 fd, 489 0 490 ); 491 if (oss->pcm_buf == MAP_FAILED) { 492 oss_logerr (errno, "Failed to map %d bytes of DAC\n", 493 hw->samples << hw->info.shift); 494 } else { 495 int err; 496 int trig = 0; 497 if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 498 oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); 499 } 500 else { 501 trig = PCM_ENABLE_OUTPUT; 502 if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 503 oss_logerr ( 504 errno, 505 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" 506 ); 507 } 508 else { 509 oss->mmapped = 1; 510 } 511 } 512 513 if (!oss->mmapped) { 514 err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); 515 if (err) { 516 oss_logerr (errno, "Failed to unmap buffer %p size %d\n", 517 oss->pcm_buf, hw->samples << hw->info.shift); 518 } 519 } 520 } 521 } 522 523 if (!oss->mmapped) { 524 oss->pcm_buf = audio_calloc ( 525 AUDIO_FUNC, 526 hw->samples, 527 1 << hw->info.shift 528 ); 529 if (!oss->pcm_buf) { 530 dolog ( 531 "Could not allocate DAC buffer (%d samples, each %d bytes)\n", 532 hw->samples, 533 1 << hw->info.shift 534 ); 535 oss_anal_close (&fd); 536 return -1; 537 } 538 } 539 540 oss->fd = fd; 541 return 0; 542 } 543 544 static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) 545 { 546 int trig; 547 OSSVoiceOut *oss = (OSSVoiceOut *) hw; 548 549 if (!oss->mmapped) { 550 return 0; 551 } 552 553 switch (cmd) { 554 case VOICE_ENABLE: 555 ldebug ("enabling voice\n"); 556 audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); 557 trig = PCM_ENABLE_OUTPUT; 558 if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 559 oss_logerr ( 560 errno, 561 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" 562 ); 563 return -1; 564 } 565 break; 566 567 case VOICE_DISABLE: 568 ldebug ("disabling voice\n"); 569 trig = 0; 570 if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { 571 oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); 572 return -1; 573 } 574 break; 575 } 576 return 0; 577 } 578 579 static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) 580 { 581 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 582 struct oss_params req, obt; 583 int endianness; 584 int err; 585 int fd; 586 audfmt_e effective_fmt; 587 struct audsettings obt_as; 588 589 oss->fd = -1; 590 591 req.fmt = aud_to_ossfmt (as->fmt); 592 req.freq = as->freq; 593 req.nchannels = as->nchannels; 594 req.fragsize = conf.fragsize; 595 req.nfrags = conf.nfrags; 596 if (oss_open (1, &req, &obt, &fd)) { 597 return -1; 598 } 599 600 err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); 601 if (err) { 602 oss_anal_close (&fd); 603 return -1; 604 } 605 606 obt_as.freq = obt.freq; 607 obt_as.nchannels = obt.nchannels; 608 obt_as.fmt = effective_fmt; 609 obt_as.endianness = endianness; 610 611 audio_pcm_init_info (&hw->info, &obt_as); 612 oss->nfrags = obt.nfrags; 613 oss->fragsize = obt.fragsize; 614 615 if (obt.nfrags * obt.fragsize & hw->info.align) { 616 dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", 617 obt.nfrags * obt.fragsize, hw->info.align + 1); 618 } 619 620 hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; 621 oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 622 if (!oss->pcm_buf) { 623 dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", 624 hw->samples, 1 << hw->info.shift); 625 oss_anal_close (&fd); 626 return -1; 627 } 628 629 oss->fd = fd; 630 return 0; 631 } 632 633 static void oss_fini_in (HWVoiceIn *hw) 634 { 635 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 636 637 oss_anal_close (&oss->fd); 638 639 if (oss->pcm_buf) { 640 qemu_free (oss->pcm_buf); 641 oss->pcm_buf = NULL; 642 } 643 } 644 645 static int oss_run_in (HWVoiceIn *hw) 646 { 647 OSSVoiceIn *oss = (OSSVoiceIn *) hw; 648 int hwshift = hw->info.shift; 649 int i; 650 int live = audio_pcm_hw_get_live_in (hw); 651 int dead = hw->samples - live; 652 size_t read_samples = 0; 653 struct { 654 int add; 655 int len; 656 } bufs[2] = { 657 { hw->wpos, 0 }, 658 { 0, 0 } 659 }; 660 661 if (!dead) { 662 return 0; 663 } 664 665 if (hw->wpos + dead > hw->samples) { 666 bufs[0].len = (hw->samples - hw->wpos) << hwshift; 667 bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift; 668 } 669 else { 670 bufs[0].len = dead << hwshift; 671 } 672 673 674 for (i = 0; i < 2; ++i) { 675 ssize_t nread; 676 677 if (bufs[i].len) { 678 void *p = advance (oss->pcm_buf, bufs[i].add << hwshift); 679 nread = read (oss->fd, p, bufs[i].len); 680 681 if (nread > 0) { 682 if (nread & hw->info.align) { 683 dolog ("warning: Misaligned read %zd (requested %d), " 684 "alignment %d\n", nread, bufs[i].add << hwshift, 685 hw->info.align + 1); 686 } 687 read_samples += nread >> hwshift; 688 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, 689 &nominal_volume); 690 } 691 692 if (bufs[i].len - nread) { 693 if (nread == -1) { 694 switch (errno) { 695 case EINTR: 696 case EAGAIN: 697 break; 698 default: 699 oss_logerr ( 700 errno, 701 "Failed to read %d bytes of audio (to %p)\n", 702 bufs[i].len, p 703 ); 704 break; 705 } 706 } 707 break; 708 } 709 } 710 } 711 712 hw->wpos = (hw->wpos + read_samples) % hw->samples; 713 return read_samples; 714 } 715 716 static int oss_read (SWVoiceIn *sw, void *buf, int size) 717 { 718 return audio_pcm_sw_read (sw, buf, size); 719 } 720 721 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) 722 { 723 (void) hw; 724 (void) cmd; 725 return 0; 726 } 727 728 static void *oss_audio_init (void) 729 { 730 return &conf; 731 } 732 733 static void oss_audio_fini (void *opaque) 734 { 735 (void) opaque; 736 } 737 738 static struct audio_option oss_options[] = { 739 {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize, 740 "Fragment size in bytes", NULL, 0}, 741 {"NFRAGS", AUD_OPT_INT, &conf.nfrags, 742 "Number of fragments", NULL, 0}, 743 {"MMAP", AUD_OPT_BOOL, &conf.try_mmap, 744 "Try using memory mapped access", NULL, 0}, 745 {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out, 746 "Path to DAC device", NULL, 0}, 747 {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, 748 "Path to ADC device", NULL, 0}, 749 {"DEBUG", AUD_OPT_BOOL, &conf.debug, 750 "Turn on some debugging messages", NULL, 0}, 751 {NULL, 0, NULL, NULL, NULL, 0} 752 }; 753 754 static struct audio_pcm_ops oss_pcm_ops = { 755 oss_init_out, 756 oss_fini_out, 757 oss_run_out, 758 oss_write, 759 oss_ctl_out, 760 761 oss_init_in, 762 oss_fini_in, 763 oss_run_in, 764 oss_read, 765 oss_ctl_in 766 }; 767 768 struct audio_driver oss_audio_driver = { 769 INIT_FIELD (name = ) "oss", 770 INIT_FIELD (descr = ) "OSS audio (www.opensound.com)", 771 INIT_FIELD (options = ) oss_options, 772 INIT_FIELD (init = ) oss_audio_init, 773 INIT_FIELD (fini = ) oss_audio_fini, 774 INIT_FIELD (pcm_ops = ) &oss_pcm_ops, 775 INIT_FIELD (can_be_default = ) 1, 776 INIT_FIELD (max_voices_out = ) INT_MAX, 777 INIT_FIELD (max_voices_in = ) INT_MAX, 778 INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut), 779 INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn) 780 }; 781