1 /* $OpenBSD$ */
2 /*
3 * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4 * Copyright (c) 2016 Tobias Kortkamp <t@tobik.me>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #ifdef USE_OSS
20 #include <sys/ioctl.h>
21 #include <sys/soundcard.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 #include "bsd-compat.h"
34
35 #define DEVPATH_PREFIX "/dev/dsp"
36 #define DEVPATH_MAX (1 + \
37 sizeof(DEVPATH_PREFIX) - 1 + \
38 sizeof(int) * 3)
39
40 struct sio_oss_fmt {
41 int fmt;
42 unsigned int bits;
43 unsigned int bps;
44 unsigned int sig;
45 unsigned int le;
46 unsigned int msb;
47 };
48
49 static struct sio_oss_fmt formats[] = {
50 /* See http://manuals.opensound.com/developer/formats.html.
51 * AFMT_{S8,U16}_* are marked as obsolete so are missing here.
52 */
53
54 /* le+msb not important */
55 { AFMT_U8, 8, 1, 0, 0, 0 },
56 { AFMT_U8, 8, 1, 0, 1, 0 },
57 { AFMT_U8, 8, 1, 0, 0, 1 },
58 { AFMT_U8, 8, 1, 0, 1, 1 },
59
60 /* msb not important */
61 { AFMT_S16_BE, 16, 2, 1, 0, 0 },
62 { AFMT_S16_BE, 16, 2, 1, 0, 1 },
63 { AFMT_S16_LE, 16, 2, 1, 1, 0 },
64 { AFMT_S16_LE, 16, 2, 1, 1, 1 },
65 { AFMT_S24_BE, 24, 3, 1, 0, 0 },
66 { AFMT_S24_BE, 24, 3, 1, 0, 1 },
67 { AFMT_S24_LE, 24, 3, 1, 1, 0 },
68 { AFMT_S24_LE, 24, 3, 1, 1, 1 },
69 { AFMT_U24_BE, 24, 3, 0, 0, 0 },
70 { AFMT_U24_BE, 24, 3, 0, 0, 1 },
71 { AFMT_U24_LE, 24, 3, 0, 1, 0 },
72 { AFMT_U24_LE, 24, 3, 0, 1, 1 },
73
74 { AFMT_S32_BE, 32, 4, 1, 0, 1 },
75 { AFMT_S32_LE, 32, 4, 1, 1, 1 },
76 { AFMT_U32_BE, 32, 4, 0, 0, 1 },
77 { AFMT_U32_LE, 32, 4, 0, 1, 1 },
78 };
79
80 struct sio_oss_hdl {
81 struct sio_hdl sio;
82 int fd;
83 int idelta, odelta;
84 int iused;
85 int oused;
86 int bpf;
87
88 int fmt;
89 unsigned int rate;
90 unsigned int chan;
91 unsigned int appbufsz;
92 unsigned int round;
93
94 int filling;
95 };
96
97 static struct sio_hdl *sio_oss_fdopen(const char *, int, unsigned int, int);
98 static int sio_oss_getcap(struct sio_hdl *, struct sio_cap *);
99 static int sio_oss_getfd(const char *, unsigned int, int);
100 static int sio_oss_getpar(struct sio_hdl *, struct sio_par *);
101 static int sio_oss_nfds(struct sio_hdl *);
102 static int sio_oss_pollfd(struct sio_hdl *, struct pollfd *, int);
103 static int sio_oss_revents(struct sio_hdl *, struct pollfd *);
104 static int sio_oss_setpar(struct sio_hdl *, struct sio_par *);
105 static int sio_oss_start(struct sio_hdl *);
106 static int sio_oss_stop(struct sio_hdl *);
107 static int sio_oss_xrun(struct sio_oss_hdl *);
108 static size_t sio_oss_read(struct sio_hdl *, void *, size_t);
109 static size_t sio_oss_write(struct sio_hdl *, const void *, size_t);
110 static void sio_oss_close(struct sio_hdl *);
111 static int sio_oss_setvol(struct sio_hdl *, unsigned int);
112 static void sio_oss_getvol(struct sio_hdl *);
113
114 static struct sio_ops sio_oss_ops = {
115 sio_oss_close,
116 sio_oss_setpar,
117 sio_oss_getpar,
118 sio_oss_getcap,
119 sio_oss_write,
120 sio_oss_read,
121 sio_oss_start,
122 sio_oss_stop,
123 sio_oss_nfds,
124 sio_oss_pollfd,
125 sio_oss_revents,
126 sio_oss_setvol,
127 sio_oss_getvol,
128 };
129
130 /*
131 * guess device capabilities
132 */
133 static int
sio_oss_getcap(struct sio_hdl * sh,struct sio_cap * cap)134 sio_oss_getcap(struct sio_hdl *sh, struct sio_cap *cap)
135 {
136 /* From sound(4):
137 * The FreeBSD multichannel matrix processor supports up to 18
138 * interleaved channels, but the limit is currently set to 8
139 * channels (as commonly used for 7.1 surround sound).
140 */
141 static unsigned int chans[] = {
142 1, 2, 4, 6, 8
143 };
144 static unsigned int rates[] = {
145 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
146 48000, 64000, 88200, 96000, 192000
147 };
148 static int afmts[] = {
149 AFMT_U8, AFMT_S16_LE, AFMT_S16_BE, AFMT_S24_LE, AFMT_U24_LE,
150 AFMT_S32_LE, AFMT_U32_LE
151 };
152 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
153 unsigned int nconf = 0;
154 unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
155 unsigned int i, j, k, conf;
156 int fmts;
157
158 if (ioctl(hdl->fd, SNDCTL_DSP_GETFMTS, &fmts) == -1) {
159 DPERROR("sio_oss_getcap: GETFMTS");
160 hdl->sio.eof = 1;
161 return 0;
162 }
163
164 /*
165 * get a subset of supported encodings
166 */
167 for (j = 0, i = 0; i < sizeof(afmts) / sizeof(afmts[0]); i++) {
168 if (fmts & afmts[i]) {
169 for (k = 0; k < sizeof(formats) / sizeof(formats[0]); k++) {
170 if (formats[k].fmt == afmts[i]) {
171 cap->enc[j].sig = formats[k].sig;
172 cap->enc[j].bits = formats[k].bits;
173 cap->enc[j].bps = formats[k].bps;
174 cap->enc[j].le = formats[k].le;
175 cap->enc[j].msb = formats[k].msb;
176 enc_map |= 1 << j;
177 j++;
178 break;
179 }
180 }
181 }
182 }
183
184 /*
185 * fill channels
186 */
187 if (hdl->sio.mode & SIO_PLAY) {
188 for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
189 cap->pchan[i] = chans[i];
190 pchan_map |= (1 << i);
191 }
192 }
193 if (hdl->sio.mode & SIO_REC) {
194 for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
195 cap->rchan[i] = chans[i];
196 rchan_map |= (1 << i);
197 }
198 }
199
200 /*
201 * fill rates
202 */
203 for (j = 0; j < sizeof(formats) / sizeof(formats[0]); j++) {
204 rate_map = 0;
205 if ((enc_map & (1 << j)) == 0)
206 continue;
207 for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
208 cap->rate[i] = rates[i];
209 rate_map |= (1 << i);
210 }
211 for (conf = 0; conf < nconf; conf++) {
212 if (cap->confs[conf].rate == rate_map) {
213 cap->confs[conf].enc |= (1 << j);
214 break;
215 }
216 }
217 if (conf == nconf) {
218 if (nconf == SIO_NCONF)
219 break;
220 cap->confs[nconf].enc = (1 << j);
221 cap->confs[nconf].pchan = pchan_map;
222 cap->confs[nconf].rchan = rchan_map;
223 cap->confs[nconf].rate = rate_map;
224 nconf++;
225 }
226 }
227 cap->nconf = nconf;
228
229 return 1;
230 }
231
232 static int
sio_oss_getfd(const char * str,unsigned int mode,int nbio)233 sio_oss_getfd(const char *str, unsigned int mode, int nbio)
234 {
235 const char *p;
236 char path[DEVPATH_MAX];
237 unsigned int devnum;
238 int fd, flags, val;
239 audio_buf_info bi;
240
241 p = _sndio_parsetype(str, "rsnd");
242 if (p == NULL) {
243 DPRINTF("sio_oss_getfd: %s: \"rsnd\" expected\n", str);
244 return -1;
245 }
246 switch (*p) {
247 case '/':
248 p++;
249 break;
250 default:
251 DPRINTF("sio_oss_getfd: %s: '/' expected\n", str);
252 return -1;
253 }
254 if (strcmp(p, "default") == 0) {
255 strlcpy(path, DEVPATH_PREFIX, sizeof(path));
256 } else {
257 p = _sndio_parsenum(p, &devnum, 255);
258 if (p == NULL || *p != '\0') {
259 DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str);
260 return -1;
261 }
262 snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
263 }
264 if (mode == (SIO_PLAY | SIO_REC))
265 flags = O_RDWR;
266 else
267 flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
268 while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) == -1) {
269 if (errno == EINTR)
270 continue;
271 DPERROR(path);
272 return -1;
273 }
274
275 /*
276 * Check if the device supports playing/recording.
277 * Unfortunately, it's possible for devices to be opened RDWR
278 * even when they don't support playing/recording.
279 */
280 if (mode & SIO_PLAY && ioctl(fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
281 close(fd);
282 return -1;
283 }
284 if (mode & SIO_REC && ioctl(fd, SNDCTL_DSP_GETISPACE, &bi) == -1) {
285 close(fd);
286 return -1;
287 }
288
289 val = 1;
290 if (ioctl(fd, SNDCTL_DSP_LOW_WATER, &val) == -1) {
291 DPERROR("sio_oss_start: LOW_WATER");
292 close(fd);
293 return -1;
294 }
295 return fd;
296 }
297
298 static struct sio_hdl *
sio_oss_fdopen(const char * str,int fd,unsigned int mode,int nbio)299 sio_oss_fdopen(const char *str, int fd, unsigned int mode, int nbio)
300 {
301 struct sio_oss_hdl *hdl;
302
303 hdl = malloc(sizeof(struct sio_oss_hdl));
304 if (hdl == NULL)
305 return NULL;
306 _sio_create(&hdl->sio, &sio_oss_ops, mode, nbio);
307
308 /* Set default device parameters */
309 hdl->fmt = AFMT_S16_LE;
310 hdl->rate = 48000;
311 hdl->chan = 2;
312 hdl->round = 960;
313 hdl->appbufsz = 8 * 960;
314 hdl->filling = 0;
315 hdl->fd = fd;
316
317 return (struct sio_hdl *)hdl;
318 }
319
320 struct sio_hdl *
_sio_oss_open(const char * str,unsigned int mode,int nbio)321 _sio_oss_open(const char *str, unsigned int mode, int nbio)
322 {
323 struct sio_oss_hdl *hdl;
324 int fd;
325
326 fd = sio_oss_getfd(str, mode, nbio);
327 if (fd == -1)
328 return NULL;
329
330 hdl = (struct sio_oss_hdl *)sio_oss_fdopen(str, fd, mode, nbio);
331 if (hdl != NULL)
332 return (struct sio_hdl*)hdl;
333
334 while (close(fd) == -1 && errno == EINTR)
335 ; /* retry */
336
337 return NULL;
338 }
339
340 static void
sio_oss_close(struct sio_hdl * sh)341 sio_oss_close(struct sio_hdl *sh)
342 {
343 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
344
345 while (close(hdl->fd) == -1 && errno == EINTR)
346 ; /* retry */
347 free(hdl);
348 }
349
350 static int
sio_oss_start(struct sio_hdl * sh)351 sio_oss_start(struct sio_hdl *sh)
352 {
353 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
354 int trig;
355
356 hdl->iused = 0;
357 hdl->oused = 0;
358 hdl->idelta = 0;
359 hdl->odelta = 0;
360
361 if (hdl->sio.mode & SIO_PLAY) {
362 /*
363 * keep the device paused and let sio_oss_pollfd() trigger the
364 * start later, to avoid buffer underruns
365 */
366 hdl->filling = 1;
367 trig = 0;
368 } else {
369 /*
370 * no play buffers to fill, start now!
371 */
372 trig = PCM_ENABLE_INPUT;
373 _sio_onmove_cb(&hdl->sio, 0);
374 }
375 if (ioctl(hdl->fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
376 DPERROR("sio_oss_start: SETTRIGGER");
377 hdl->sio.eof = 1;
378 return 0;
379 }
380 return 1;
381 }
382
383 static int
sio_oss_stop(struct sio_hdl * sh)384 sio_oss_stop(struct sio_hdl *sh)
385 {
386 struct sio_oss_hdl *hdl = (struct sio_oss_hdl*)sh;
387 int trig;
388
389 if (hdl->filling) {
390 hdl->filling = 0;
391 return 1;
392 }
393 trig = 0;
394 if (ioctl(hdl->fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
395 DPERROR("sio_oss_stop: SETTRIGGER");
396 hdl->sio.eof = 1;
397 return 0;
398 }
399 return 1;
400 }
401
402 static int
sio_oss_setpar(struct sio_hdl * sh,struct sio_par * par)403 sio_oss_setpar(struct sio_hdl *sh, struct sio_par *par)
404 {
405 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
406 unsigned int i, round, bufsz;
407 int frag_max, frag_shift, frag_count, frag;
408 unsigned int le, sig, msb;
409
410 le = par->le;
411 sig = par->sig;
412 msb = par->msb;
413
414 if (le == ~0U)
415 le = 0;
416 if (sig == ~0U)
417 sig = 0;
418 if (msb == ~0U)
419 msb = 0;
420
421 hdl->fmt = AFMT_S16_LE;
422 for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
423 if (formats[i].bits == par->bits &&
424 formats[i].le == le &&
425 formats[i].sig == sig &&
426 formats[i].msb == msb) {
427 hdl->fmt = formats[i].fmt;
428 break;
429 }
430 }
431
432 if (par->rate != ~0U)
433 hdl->rate = par->rate;
434 if (hdl->rate < 8000)
435 hdl->rate = 8000;
436 if (hdl->rate > 192000)
437 hdl->rate = 192000;
438
439 if ((hdl->sio.mode & SIO_PLAY) && par->pchan != ~0U)
440 hdl->chan = par->pchan;
441 else if ((hdl->sio.mode & SIO_REC) && par->rchan != ~0U)
442 hdl->chan = par->rchan;
443
444 if (ioctl(hdl->fd, SNDCTL_DSP_SETFMT, &hdl->fmt) == -1) {
445 DPERROR("sio_oss_setpar: SETFMT");
446 hdl->sio.eof = 1;
447 return 0;
448 }
449
450 for (i = 0; ; i++) {
451 if (i == sizeof(formats) / sizeof(formats[0])) {
452 DPRINTF("sio_oss_setpar: unknown fmt %d\n", hdl->fmt);
453 hdl->sio.eof = 1;
454 return 0;
455 }
456 if (formats[i].fmt == hdl->fmt)
457 break;
458 }
459
460 if (ioctl(hdl->fd, SNDCTL_DSP_SPEED, &hdl->rate) == -1) {
461 DPERROR("sio_oss_setpar: SPEED");
462 hdl->sio.eof = 1;
463 return 0;
464 }
465
466 if (ioctl(hdl->fd, SNDCTL_DSP_CHANNELS, &hdl->chan) == -1) {
467 DPERROR("sio_oss_setpar: CHANNELS");
468 hdl->sio.eof = 1;
469 return 0;
470 }
471
472 hdl->bpf = formats[i].bps * hdl->chan;
473
474 if (par->round != ~0U && par->appbufsz != ~0U) {
475 round = par->round;
476 bufsz = par->appbufsz;
477 } else if (par->round != ~0U) {
478 round = par->round;
479 bufsz = 2 * par->round;
480 } else if (par->appbufsz != ~0U) {
481 round = par->appbufsz / 2;
482 bufsz = par->appbufsz;
483 } else {
484 /*
485 * even if it's not specified, we have to set the
486 * block size to ensure that both play and record
487 * direction get the same block size. Pick an
488 * arbitrary value that would work for most players at
489 * 48kHz, stereo, 16-bit.
490 */
491 round = 512;
492 bufsz = 1024;
493 }
494
495 frag_max = round * hdl->chan * formats[i].bps;
496 frag_shift = 8;
497 while (1 << (frag_shift + 1) <= frag_max)
498 frag_shift++;
499
500 frag_count = bufsz / round;
501 if (frag_count < 2)
502 frag_count = 2;
503
504 frag = frag_count << 16 | frag_shift;
505 if (ioctl(hdl->fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) {
506 DPERROR("sio_oss_setpar: SETFRAGMENT");
507 hdl->sio.eof = 1;
508 return 0;
509 }
510
511 return 1;
512 }
513
514 static int
sio_oss_getpar(struct sio_hdl * sh,struct sio_par * par)515 sio_oss_getpar(struct sio_hdl *sh, struct sio_par *par)
516 {
517 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
518 unsigned int i, found = 0;
519 audio_buf_info pbi, rbi;
520
521 for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
522 if (formats[i].fmt == hdl->fmt) {
523 par->sig = formats[i].sig;
524 par->le = formats[i].le;
525 par->bits = formats[i].bits;
526 par->bps = formats[i].bps;
527 par->msb = formats[i].msb;
528 found = 1;
529 break;
530 }
531 }
532 if (!found) {
533 DPRINTF("sio_oss_getpar: unknown format %d\n", hdl->fmt);
534 hdl->sio.eof = 1;
535 return 0;
536 }
537
538 par->rate = hdl->rate;
539 par->pchan = hdl->chan;
540 par->rchan = hdl->chan;
541 par->xrun = SIO_IGNORE;
542
543 if (hdl->sio.mode & SIO_PLAY) {
544 if (ioctl(hdl->fd, SNDCTL_DSP_GETOSPACE, &pbi) == -1) {
545 DPERROR("sio_oss_getpar: SNDCTL_DSP_GETOSPACE");
546 hdl->sio.eof = 1;
547 return 0;
548 }
549 par->round = pbi.fragsize / (par->pchan * par->bps);
550 par->bufsz = pbi.fragstotal * par->round;
551 }
552 if (hdl->sio.mode & SIO_REC) {
553 if (ioctl(hdl->fd, SNDCTL_DSP_GETISPACE, &rbi) == -1) {
554 DPERROR("sio_oss_getpar: SNDCTL_DSP_GETISPACE");
555 hdl->sio.eof = 1;
556 return 0;
557 }
558 if (!(hdl->sio.mode & SIO_PLAY)) {
559 par->round = rbi.fragsize / (par->rchan * par->bps);
560 par->bufsz = rbi.fragstotal * par->round;
561 }
562 }
563 par->appbufsz = par->bufsz;
564 #ifdef DEBUG
565 if ((hdl->sio.mode & (SIO_REC | SIO_PLAY)) == (SIO_REC | SIO_PLAY)) {
566 if (pbi.fragsize != rbi.fragsize) {
567 DPRINTF("sio_oss_getpar: frag size/count mismatch\n"
568 "play: count = %d, size = %d\n"
569 "rec: count = %d, size = %d\n",
570 pbi.fragstotal, pbi.fragsize,
571 rbi.fragstotal, rbi.fragsize);
572 hdl->sio.eof = 1;
573 return 0;
574 }
575 }
576 #endif
577 return 1;
578 }
579
580 static size_t
sio_oss_read(struct sio_hdl * sh,void * buf,size_t len)581 sio_oss_read(struct sio_hdl *sh, void *buf, size_t len)
582 {
583 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
584 ssize_t n;
585
586 while ((n = read(hdl->fd, buf, len)) == -1) {
587 if (errno == EINTR)
588 continue;
589 if (errno != EAGAIN) {
590 DPERROR("sio_oss_read: read");
591 hdl->sio.eof = 1;
592 }
593 return 0;
594 }
595 if (n == 0) {
596 DPRINTF("sio_oss_read: eof\n");
597 hdl->sio.eof = 1;
598 return 0;
599 }
600
601 hdl->idelta += n;
602 return n;
603 }
604
605 static size_t
sio_oss_write(struct sio_hdl * sh,const void * buf,size_t len)606 sio_oss_write(struct sio_hdl *sh, const void *buf, size_t len)
607 {
608 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
609 const unsigned char *data = buf;
610 ssize_t n, todo;
611
612 todo = len;
613 while ((n = write(hdl->fd, data, todo)) == -1) {
614 if (errno == EINTR)
615 continue;
616 if (errno != EAGAIN) {
617 DPERROR("sio_oss_write: write");
618 hdl->sio.eof = 1;
619 }
620 return 0;
621 }
622
623 hdl->odelta += n;
624 return n;
625 }
626
627 static int
sio_oss_nfds(struct sio_hdl * hdl)628 sio_oss_nfds(struct sio_hdl *hdl)
629 {
630 return 1;
631 }
632
633 static int
sio_oss_pollfd(struct sio_hdl * sh,struct pollfd * pfd,int events)634 sio_oss_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
635 {
636 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
637 int trig;
638
639 pfd->fd = hdl->fd;
640 pfd->events = events;
641 if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz *
642 hdl->sio.par.pchan * hdl->sio.par.bps) {
643 hdl->filling = 0;
644 trig = 0;
645 if (hdl->sio.mode & SIO_PLAY)
646 trig |= PCM_ENABLE_OUTPUT;
647 if (hdl->sio.mode & SIO_REC)
648 trig |= PCM_ENABLE_INPUT;
649 if (ioctl(hdl->fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
650 DPERROR("sio_oss_pollfd: SETTRIGGER");
651 hdl->sio.eof = 1;
652 return 0;
653 }
654 _sio_onmove_cb(&hdl->sio, 0);
655 }
656 return 1;
657 }
658
659 static int
sio_oss_xrun(struct sio_oss_hdl * hdl)660 sio_oss_xrun(struct sio_oss_hdl *hdl)
661 {
662 int clk;
663 int wsil, rdrop, cmove;
664 int rbpf, rround;
665 int wbpf;
666
667 DPRINTFN(2, "sio_oss_xrun:\n");
668 #ifdef DEBUG
669 if (_sndio_debug >= 2)
670 _sio_printpos(&hdl->sio);
671 #endif
672
673 /*
674 * we assume rused/wused are zero if rec/play modes are not
675 * selected. This allows us to keep the same formula for all
676 * modes, provided we set rbpf/wbpf to 1 to avoid division by
677 * zero.
678 *
679 * to understand the formula, draw a picture :)
680 */
681 rbpf = (hdl->sio.mode & SIO_REC) ?
682 hdl->sio.par.bps * hdl->sio.par.rchan : 1;
683 wbpf = (hdl->sio.mode & SIO_PLAY) ?
684 hdl->sio.par.bps * hdl->sio.par.pchan : 1;
685 rround = hdl->sio.par.round * rbpf;
686
687 clk = hdl->sio.cpos % hdl->sio.par.round;
688 rdrop = (clk * rbpf - hdl->sio.rused) % rround;
689 if (rdrop < 0)
690 rdrop += rround;
691 cmove = (rdrop + hdl->sio.rused) / rbpf;
692 wsil = cmove * wbpf + hdl->sio.wused;
693
694 DPRINTFN(2, "wsil = %d, cmove = %d, rdrop = %d\n", wsil, cmove, rdrop);
695
696 if (!sio_oss_stop(&hdl->sio))
697 return 0;
698 if (!sio_oss_start(&hdl->sio))
699 return 0;
700 if (hdl->sio.mode & SIO_PLAY) {
701 hdl->odelta -= cmove * hdl->bpf;
702 hdl->sio.wsil = wsil;
703 }
704 if (hdl->sio.mode & SIO_REC) {
705 hdl->idelta -= cmove * hdl->bpf;
706 hdl->sio.rdrop = rdrop;
707 }
708 DPRINTFN(2, "xrun: corrected\n");
709 DPRINTFN(2, "wsil = %d, rdrop = %d, odelta = %d, idelta = %d\n",
710 wsil, rdrop, hdl->odelta, hdl->idelta);
711 return 1;
712 }
713
714 static int
sio_oss_revents(struct sio_hdl * sh,struct pollfd * pfd)715 sio_oss_revents(struct sio_hdl *sh, struct pollfd *pfd)
716 {
717 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
718 audio_errinfo ei;
719 int delta, iused, oused;
720 int revents = pfd->revents;
721 oss_count_t optr, iptr;
722
723 if ((pfd->revents & POLLHUP) ||
724 (pfd->revents & (POLLIN | POLLOUT)) == 0)
725 return pfd->revents;
726
727 /* Hide xruns from clients */
728 if (ioctl(hdl->fd, SNDCTL_DSP_GETERROR, &ei) == -1) {
729 DPERROR("sio_oss_revents: GETERROR");
730 hdl->sio.eof = 1;
731 return POLLHUP;
732 }
733 if (ei.play_underruns > 0 || ei.rec_overruns > 0) {
734 if (!sio_oss_xrun(hdl))
735 return POLLHUP;
736 return 0;
737 }
738
739 if (hdl->sio.mode & SIO_PLAY) {
740 if (ioctl(hdl->fd, SNDCTL_DSP_CURRENT_OPTR, &optr) == -1) {
741 DPERROR("sio_oss_revents: CURRENT_OPTR");
742 hdl->sio.eof = 1;
743 return POLLHUP;
744 }
745 oused = optr.fifo_samples * hdl->bpf;
746 hdl->odelta -= oused - hdl->oused;
747 hdl->oused = oused;
748 if (!(hdl->sio.mode & SIO_REC)) {
749 hdl->idelta = hdl->odelta;
750 }
751 }
752 if (hdl->sio.mode & SIO_REC) {
753 if (ioctl(hdl->fd, SNDCTL_DSP_CURRENT_IPTR, &iptr) == -1) {
754 DPERROR("sio_oss_revents: CURRENT_IPTR");
755 hdl->sio.eof = 1;
756 return POLLHUP;
757 }
758 iused = iptr.fifo_samples * hdl->bpf;
759 hdl->idelta += iused - hdl->iused;
760 hdl->iused = iused;
761 if (!(hdl->sio.mode & SIO_PLAY)) {
762 hdl->odelta = hdl->idelta;
763 }
764 }
765
766 delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
767 if (delta > 0) {
768 _sio_onmove_cb(&hdl->sio, delta / hdl->bpf);
769 hdl->idelta -= delta;
770 hdl->odelta -= delta;
771 }
772 return revents;
773 }
774
775 static int
sio_oss_setvol(struct sio_hdl * sh,unsigned int vol)776 sio_oss_setvol(struct sio_hdl *sh, unsigned int vol)
777 {
778 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
779 int newvol;
780
781 /* Scale to 0..100 */
782 newvol = (100 * vol + SIO_MAXVOL / 2) / SIO_MAXVOL;
783 newvol = newvol | (newvol << 8);
784
785 if (ioctl(hdl->fd, SNDCTL_DSP_SETPLAYVOL, &newvol) == -1) {
786 DPERROR("sio_oss_setvol");
787 hdl->sio.eof = 1;
788 return 0;
789 }
790
791 return 1;
792 }
793
794 static void
sio_oss_getvol(struct sio_hdl * sh)795 sio_oss_getvol(struct sio_hdl *sh)
796 {
797 struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
798 int vol;
799
800 if (ioctl(hdl->fd, SNDCTL_DSP_GETPLAYVOL, &vol) == -1) {
801 DPERROR("sio_oss_getvol");
802 hdl->sio.eof = 1;
803 return;
804 }
805
806 /* Use left channel volume and scale to SIO_MAXVOL */
807 vol = (SIO_MAXVOL * (vol & 0x7f) + 50) / 100;
808 _sio_onvol_cb(&hdl->sio, vol);
809 }
810
811 #endif /* defined USE_OSS */
812