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