GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libsndio/sio_sun.c Lines: 0 305 0.0 %
Date: 2017-11-07 Branches: 0 234 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sio_sun.c,v 1.26 2016/04/07 06:30:13 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
#include <sys/types.h>
19
#include <sys/ioctl.h>
20
#include <sys/audioio.h>
21
#include <sys/stat.h>
22
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <limits.h>
26
#include <poll.h>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <unistd.h>
31
32
#include "debug.h"
33
#include "sio_priv.h"
34
35
#define DEVPATH_PREFIX	"/dev/audio"
36
#define DEVPATH_MAX 	(1 +		\
37
	sizeof(DEVPATH_PREFIX) - 1 +	\
38
	sizeof(int) * 3)
39
40
struct sio_sun_hdl {
41
	struct sio_hdl sio;
42
	int fd;
43
	int filling;
44
	unsigned int ibpf, obpf;	/* bytes per frame */
45
	unsigned int ibytes, obytes;	/* bytes the hw transferred */
46
	unsigned int ierr, oerr;	/* frames the hw dropped */
47
	int idelta, odelta;		/* position reported to client */
48
};
49
50
static void sio_sun_close(struct sio_hdl *);
51
static int sio_sun_start(struct sio_hdl *);
52
static int sio_sun_stop(struct sio_hdl *);
53
static int sio_sun_setpar(struct sio_hdl *, struct sio_par *);
54
static int sio_sun_getpar(struct sio_hdl *, struct sio_par *);
55
static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *);
56
static size_t sio_sun_read(struct sio_hdl *, void *, size_t);
57
static size_t sio_sun_write(struct sio_hdl *, const void *, size_t);
58
static int sio_sun_nfds(struct sio_hdl *);
59
static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int);
60
static int sio_sun_revents(struct sio_hdl *, struct pollfd *);
61
62
static struct sio_ops sio_sun_ops = {
63
	sio_sun_close,
64
	sio_sun_setpar,
65
	sio_sun_getpar,
66
	sio_sun_getcap,
67
	sio_sun_write,
68
	sio_sun_read,
69
	sio_sun_start,
70
	sio_sun_stop,
71
	sio_sun_nfds,
72
	sio_sun_pollfd,
73
	sio_sun_revents,
74
	NULL, /* setvol */
75
	NULL, /* getvol */
76
};
77
78
static int
79
sio_sun_adjpar(struct sio_sun_hdl *hdl, struct audio_swpar *ap)
80
{
81
	if (hdl->sio.eof)
82
		return 0;
83
	if (ioctl(hdl->fd, AUDIO_SETPAR, ap)) {
84
		DPERROR("AUDIO_SETPAR");
85
		hdl->sio.eof = 1;
86
		return 0;
87
	}
88
	if (ioctl(hdl->fd, AUDIO_GETPAR, ap)) {
89
		DPERROR("AUDIO_GETPAR");
90
		hdl->sio.eof = 1;
91
		return 0;
92
	}
93
	return 1;
94
}
95
96
/*
97
 * try to set the device to the given parameters and check that the
98
 * device can use them; return 1 on success, 0 on failure or error
99
 */
100
static int
101
sio_sun_testpar(struct sio_sun_hdl *hdl, struct sio_enc *enc,
102
    unsigned int pchan, unsigned int rchan, unsigned int rate)
103
{
104
	struct audio_swpar ap;
105
106
	AUDIO_INITPAR(&ap);
107
	if (enc != NULL) {
108
		ap.sig = enc->sig;
109
		ap.bits = enc->bits;
110
		ap.bps = enc->bps;
111
		if (ap.bps > 1)
112
			ap.le = enc->le;
113
		if (ap.bps * 8 > ap.bits)
114
			ap.msb = enc->msb;
115
	}
116
	if (rate)
117
		ap.rate = rate;
118
	if (pchan && (hdl->sio.mode & SIO_PLAY))
119
		ap.pchan = pchan;
120
	if (rchan && (hdl->sio.mode & SIO_REC))
121
		ap.rchan = rchan;
122
	if (!sio_sun_adjpar(hdl, &ap))
123
		return 0;
124
	if (pchan && ap.pchan != pchan)
125
		return 0;
126
	if (rchan && ap.rchan != rchan)
127
		return 0;
128
	if (rate && ap.rate != rate)
129
		return 0;
130
	if (enc) {
131
		if (ap.sig != enc->sig)
132
			return 0;
133
		if (ap.bits != enc->bits)
134
			return 0;
135
		if (ap.bps != enc->bps)
136
			return 0;
137
		if (ap.bps > 1 && ap.le != enc->le)
138
			return 0;
139
		if (ap.bits < ap.bps * 8 && ap.msb != enc->msb)
140
			return 0;
141
	}
142
	return 1;
143
}
144
145
/*
146
 * guess device capabilities
147
 */
148
static int
149
sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap)
150
{
151
	static unsigned int chans[] = {
152
		1, 2, 4, 6, 8, 10, 12
153
	};
154
	static unsigned int rates[] = {
155
		8000, 11025, 12000, 16000, 22050, 24000,
156
		32000, 44100, 48000, 64000, 88200, 96000
157
	};
158
	static unsigned int encs[] = {
159
		8, 16, 24, 32
160
	};
161
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
162
	struct audio_swpar savepar, ap;
163
	unsigned int nconf = 0;
164
	unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
165
	unsigned int i, j, conf;
166
167
	if (ioctl(hdl->fd, AUDIO_GETPAR, &savepar)) {
168
		DPERROR("AUDIO_GETPAR");
169
		hdl->sio.eof = 1;
170
		return 0;
171
	}
172
173
	/*
174
	 * get a subset of supported encodings
175
	 */
176
	for (i = 0; i < sizeof(encs) / sizeof(encs[0]); i++) {
177
		AUDIO_INITPAR(&ap);
178
		ap.bits = encs[i];
179
		ap.sig = (ap.bits > 8) ? 1 : 0;
180
		if (!sio_sun_adjpar(hdl, &ap))
181
			return 0;
182
		if (ap.bits == encs[i]) {
183
			cap->enc[i].sig = ap.sig;
184
			cap->enc[i].bits = ap.bits;
185
			cap->enc[i].le = ap.le;
186
			cap->enc[i].bps = ap.bps;
187
			cap->enc[i].msb = ap.msb;
188
			enc_map |= 1 << i;
189
		}
190
	}
191
192
	/*
193
	 * fill channels
194
	 *
195
	 * for now we're lucky: all kernel devices assume that the
196
	 * number of channels and the encoding are independent so we can
197
	 * use the current encoding and try various channels.
198
	 */
199
	if (hdl->sio.mode & SIO_PLAY) {
200
		for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
201
			AUDIO_INITPAR(&ap);
202
			ap.pchan = chans[i];
203
			if (!sio_sun_adjpar(hdl, &ap))
204
				return 0;
205
			if (ap.pchan == chans[i]) {
206
				cap->pchan[i] = chans[i];
207
				pchan_map |= (1 << i);
208
			}
209
		}
210
	}
211
	if (hdl->sio.mode & SIO_REC) {
212
		for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
213
			AUDIO_INITPAR(&ap);
214
			ap.pchan = chans[i];
215
			if (!sio_sun_adjpar(hdl, &ap))
216
				return 0;
217
			if (ap.rchan == chans[i]) {
218
				cap->rchan[i] = chans[i];
219
				rchan_map |= (1 << i);
220
			}
221
		}
222
	}
223
224
	/*
225
	 * fill rates
226
	 *
227
	 * rates are not independent from other parameters (eg. on
228
	 * uaudio devices), so certain rates may not be allowed with
229
	 * certain encodings. We have to check rates for all encodings
230
	 */
231
	for (j = 0; j < sizeof(encs) / sizeof(encs[0]); j++) {
232
		rate_map = 0;
233
		if ((enc_map & (1 << j)) == 0)
234
			continue;
235
		for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
236
			if (sio_sun_testpar(hdl,
237
				&cap->enc[j], 0, 0, rates[i])) {
238
				cap->rate[i] = rates[i];
239
				rate_map |= (1 << i);
240
			}
241
		}
242
		for (conf = 0; conf < nconf; conf++) {
243
			if (cap->confs[conf].rate == rate_map) {
244
				cap->confs[conf].enc |= (1 << j);
245
				break;
246
			}
247
		}
248
		if (conf == nconf) {
249
			if (nconf == SIO_NCONF)
250
				break;
251
			cap->confs[nconf].enc = (1 << j);
252
			cap->confs[nconf].pchan = pchan_map;
253
			cap->confs[nconf].rchan = rchan_map;
254
			cap->confs[nconf].rate = rate_map;
255
			nconf++;
256
		}
257
	}
258
	cap->nconf = nconf;
259
260
	if (ioctl(hdl->fd, AUDIO_SETPAR, &savepar)) {
261
		DPERROR("AUDIO_SETPAR");
262
		hdl->sio.eof = 1;
263
		return 0;
264
	}
265
	return 1;
266
}
267
268
int
269
sio_sun_getfd(const char *str, unsigned int mode, int nbio)
270
{
271
	const char *p;
272
	char path[DEVPATH_MAX];
273
	unsigned int devnum;
274
	int fd, flags;
275
276
#ifdef DEBUG
277
	_sndio_debug_init();
278
#endif
279
	p = _sndio_parsetype(str, "rsnd");
280
	if (p == NULL) {
281
		DPRINTF("sio_sun_getfd: %s: \"rsnd\" expected\n", str);
282
		return -1;
283
	}
284
	switch (*p) {
285
	case '/':
286
		p++;
287
		break;
288
	default:
289
		DPRINTF("sio_sun_getfd: %s: '/' expected\n", str);
290
		return -1;
291
	}
292
	p = _sndio_parsenum(p, &devnum, 255);
293
	if (p == NULL || *p != '\0') {
294
		DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str);
295
		return -1;
296
	}
297
	snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
298
	if (mode == (SIO_PLAY | SIO_REC))
299
		flags = O_RDWR;
300
	else
301
		flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
302
	while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) < 0) {
303
		if (errno == EINTR)
304
			continue;
305
		DPERROR(path);
306
		return -1;
307
	}
308
	return fd;
309
}
310
311
struct sio_hdl *
312
sio_sun_fdopen(int fd, unsigned int mode, int nbio)
313
{
314
	struct sio_sun_hdl *hdl;
315
316
#ifdef DEBUG
317
	_sndio_debug_init();
318
#endif
319
	hdl = malloc(sizeof(struct sio_sun_hdl));
320
	if (hdl == NULL)
321
		return NULL;
322
	_sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
323
324
	/*
325
	 * pause the device
326
	 */
327
	if (ioctl(fd, AUDIO_STOP) < 0) {
328
		DPERROR("AUDIO_STOP");
329
		free(hdl);
330
		return NULL;
331
	}
332
	hdl->fd = fd;
333
	hdl->filling = 0;
334
	return (struct sio_hdl *)hdl;
335
}
336
337
struct sio_hdl *
338
_sio_sun_open(const char *str, unsigned int mode, int nbio)
339
{
340
	struct sio_hdl *hdl;
341
	int fd;
342
343
	fd = sio_sun_getfd(str, mode, nbio);
344
	if (fd < 0)
345
		return NULL;
346
	hdl = sio_sun_fdopen(fd, mode, nbio);
347
	if (hdl != NULL)
348
		return hdl;
349
	while (close(fd) < 0 && errno == EINTR)
350
		; /* retry */
351
	return NULL;
352
}
353
354
static void
355
sio_sun_close(struct sio_hdl *sh)
356
{
357
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
358
359
	while (close(hdl->fd) < 0 && errno == EINTR)
360
		; /* retry */
361
	free(hdl);
362
}
363
364
static int
365
sio_sun_start(struct sio_hdl *sh)
366
{
367
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
368
369
	hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
370
	hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
371
	hdl->ibytes = 0;
372
	hdl->obytes = 0;
373
	hdl->ierr = 0;
374
	hdl->oerr = 0;
375
	hdl->idelta = 0;
376
	hdl->odelta = 0;
377
378
	if (hdl->sio.mode & SIO_PLAY) {
379
		/*
380
		 * keep the device paused and let sio_sun_pollfd() trigger the
381
		 * start later, to avoid buffer underruns
382
		 */
383
		hdl->filling = 1;
384
	} else {
385
		/*
386
		 * no play buffers to fill, start now!
387
		 */
388
		if (ioctl(hdl->fd, AUDIO_START) < 0) {
389
			DPERROR("AUDIO_START");
390
			hdl->sio.eof = 1;
391
			return 0;
392
		}
393
		_sio_onmove_cb(&hdl->sio, 0);
394
	}
395
	return 1;
396
}
397
398
static int
399
sio_sun_stop(struct sio_hdl *sh)
400
{
401
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
402
403
	if (hdl->filling) {
404
		hdl->filling = 0;
405
		return 1;
406
	}
407
	if (ioctl(hdl->fd, AUDIO_STOP) < 0) {
408
		DPERROR("AUDIO_STOP");
409
		hdl->sio.eof = 1;
410
		return 0;
411
	}
412
	return 1;
413
}
414
415
static int
416
sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
417
{
418
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
419
	struct audio_swpar ap;
420
421
	AUDIO_INITPAR(&ap);
422
	ap.sig = par->sig;
423
	ap.le = par->le;
424
	ap.bits = par->bits;
425
	ap.bps = par->bps;
426
	ap.msb = par->msb;
427
	ap.rate = par->rate;
428
	if (hdl->sio.mode & SIO_PLAY)
429
		ap.pchan = par->pchan;
430
	if (hdl->sio.mode & SIO_REC)
431
		ap.rchan = par->rchan;
432
	if (par->round != ~0U && par->appbufsz != ~0U) {
433
		ap.round = par->round;
434
		ap.nblks = par->appbufsz / par->round;
435
	} else if (par->round != ~0U) {
436
		ap.round = par->round;
437
		ap.nblks = 2;
438
	} else if (par->appbufsz != ~0U) {
439
		ap.round = par->appbufsz / 2;
440
		ap.nblks = 2;
441
	}
442
	if (ioctl(hdl->fd, AUDIO_SETPAR, &ap) < 0) {
443
		DPERROR("AUDIO_SETPAR");
444
		hdl->sio.eof = 1;
445
		return 0;
446
	}
447
	return 1;
448
}
449
450
static int
451
sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
452
{
453
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
454
	struct audio_swpar ap;
455
456
	if (ioctl(hdl->fd, AUDIO_GETPAR, &ap) < 0) {
457
		DPERROR("AUDIO_GETPAR");
458
		hdl->sio.eof = 1;
459
		return 0;
460
	}
461
	par->sig = ap.sig;
462
	par->le = ap.le;
463
	par->bits = ap.bits;
464
	par->bps = ap.bps;
465
	par->msb = ap.msb;
466
	par->rate = ap.rate;
467
	par->pchan = ap.pchan;
468
	par->rchan = ap.rchan;
469
	par->round = ap.round;
470
	par->appbufsz = par->bufsz = ap.nblks * ap.round;
471
	par->xrun = SIO_IGNORE;
472
	return 1;
473
}
474
475
static size_t
476
sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
477
{
478
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
479
	ssize_t n;
480
481
	while ((n = read(hdl->fd, buf, len)) < 0) {
482
		if (errno == EINTR)
483
			continue;
484
		if (errno != EAGAIN) {
485
			DPERROR("sio_sun_read: read");
486
			hdl->sio.eof = 1;
487
		}
488
		return 0;
489
	}
490
	if (n == 0) {
491
		DPRINTF("sio_sun_read: eof\n");
492
		hdl->sio.eof = 1;
493
		return 0;
494
	}
495
	return n;
496
}
497
498
static size_t
499
sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
500
{
501
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
502
	const unsigned char *data = buf;
503
	ssize_t n, todo;
504
505
	todo = len;
506
	while ((n = write(hdl->fd, data, todo)) < 0) {
507
		if (errno == EINTR)
508
			continue;
509
		if (errno != EAGAIN) {
510
			DPERROR("sio_sun_write: write");
511
			hdl->sio.eof = 1;
512
		}
513
		return 0;
514
	}
515
	return n;
516
}
517
518
static int
519
sio_sun_nfds(struct sio_hdl *hdl)
520
{
521
	return 1;
522
}
523
524
static int
525
sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
526
{
527
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
528
529
	pfd->fd = hdl->fd;
530
	pfd->events = events;
531
	if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz *
532
		hdl->sio.par.pchan * hdl->sio.par.bps) {
533
		hdl->filling = 0;
534
		if (ioctl(hdl->fd, AUDIO_START) < 0) {
535
			DPERROR("AUDIO_START");
536
			hdl->sio.eof = 1;
537
			return 0;
538
		}
539
		_sio_onmove_cb(&hdl->sio, 0);
540
	}
541
	return 1;
542
}
543
544
int
545
sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
546
{
547
	struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
548
	struct audio_pos ap;
549
	int dierr = 0, doerr = 0, offset, delta;
550
	int revents = pfd->revents;
551
552
	if ((pfd->revents & POLLHUP) ||
553
	    (pfd->revents & (POLLIN | POLLOUT)) == 0)
554
		return pfd->revents;
555
	if (ioctl(hdl->fd, AUDIO_GETPOS, &ap) < 0) {
556
		DPERROR("sio_sun_revents: GETPOS");
557
		hdl->sio.eof = 1;
558
		return POLLHUP;
559
	}
560
	if (hdl->sio.mode & SIO_PLAY) {
561
		delta = (ap.play_pos - hdl->obytes) / hdl->obpf;
562
		doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
563
		hdl->obytes = ap.play_pos;
564
		hdl->oerr = ap.play_xrun;
565
		hdl->odelta += delta;
566
		if (!(hdl->sio.mode & SIO_REC)) {
567
			hdl->idelta += delta;
568
			dierr = doerr;
569
		}
570
		if (doerr > 0)
571
			DPRINTFN(2, "play xrun %d\n", doerr);
572
	}
573
	if (hdl->sio.mode & SIO_REC) {
574
		delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf;
575
		dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf;
576
		hdl->ibytes = ap.rec_pos;
577
		hdl->ierr = ap.rec_xrun;
578
		hdl->idelta += delta;
579
		if (!(hdl->sio.mode & SIO_PLAY)) {
580
			hdl->odelta += delta;
581
			doerr = dierr;
582
		}
583
		if (dierr > 0)
584
			DPRINTFN(2, "rec xrun %d\n", dierr);
585
	}
586
587
	/*
588
	 * GETPOS reports positions including xruns,
589
	 * so we have to substract to get the real position
590
	 */
591
	hdl->idelta -= dierr;
592
	hdl->odelta -= doerr;
593
594
	offset = doerr - dierr;
595
	if (offset > 0) {
596
		hdl->sio.rdrop += offset * hdl->ibpf;
597
		hdl->idelta -= offset;
598
		DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr);
599
	} else if (offset < 0) {
600
		hdl->sio.wsil += -offset * hdl->obpf;
601
		hdl->odelta -= -offset;
602
		DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
603
	}
604
605
	delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
606
	if (delta > 0) {
607
		_sio_onmove_cb(&hdl->sio, delta);
608
		hdl->idelta -= delta;
609
		hdl->odelta -= delta;
610
	}
611
	return revents;
612
}