GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libsndio/sio.c Lines: 0 275 0.0 %
Date: 2017-11-13 Branches: 0 240 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sio.c,v 1.21 2016/01/09 08:27:24 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/stat.h>
20
21
#include <errno.h>
22
#include <fcntl.h>
23
#include <poll.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <time.h>
28
#include <unistd.h>
29
30
#include "debug.h"
31
#include "sio_priv.h"
32
33
#define SIO_PAR_MAGIC	0x83b905a4
34
35
void
36
sio_initpar(struct sio_par *par)
37
{
38
	memset(par, 0xff, sizeof(struct sio_par));
39
	par->__magic = SIO_PAR_MAGIC;
40
}
41
42
struct sio_hdl *
43
sio_open(const char *str, unsigned int mode, int nbio)
44
{
45
	static char devany[] = SIO_DEVANY;
46
	struct sio_hdl *hdl;
47
48
#ifdef DEBUG
49
	_sndio_debug_init();
50
#endif
51
	if ((mode & (SIO_PLAY | SIO_REC)) == 0)
52
		return NULL;
53
	if (str == NULL) /* backward compat */
54
		str = devany;
55
	if (strcmp(str, devany) == 0 && !issetugid()) {
56
		str = getenv("AUDIODEVICE");
57
		if (str == NULL)
58
			str = devany;
59
	}
60
	if (strcmp(str, devany) == 0) {
61
		hdl = _sio_aucat_open("snd/0", mode, nbio);
62
		if (hdl != NULL)
63
			return hdl;
64
		return _sio_sun_open("rsnd/0", mode, nbio);
65
	}
66
	if (_sndio_parsetype(str, "snd"))
67
		return _sio_aucat_open(str, mode, nbio);
68
	if (_sndio_parsetype(str, "rsnd"))
69
		return _sio_sun_open(str, mode, nbio);
70
	DPRINTF("sio_open: %s: unknown device type\n", str);
71
	return NULL;
72
}
73
74
void
75
_sio_create(struct sio_hdl *hdl, struct sio_ops *ops,
76
    unsigned int mode, int nbio)
77
{
78
	hdl->ops = ops;
79
	hdl->mode = mode;
80
	hdl->nbio = nbio;
81
	hdl->started = 0;
82
	hdl->eof = 0;
83
	hdl->move_cb = NULL;
84
	hdl->vol_cb = NULL;
85
}
86
87
void
88
sio_close(struct sio_hdl *hdl)
89
{
90
	hdl->ops->close(hdl);
91
}
92
93
int
94
sio_start(struct sio_hdl *hdl)
95
{
96
#ifdef DEBUG
97
	struct timespec ts;
98
#endif
99
100
	if (hdl->eof) {
101
		DPRINTF("sio_start: eof\n");
102
		return 0;
103
	}
104
	if (hdl->started) {
105
		DPRINTF("sio_start: already started\n");
106
		hdl->eof = 1;
107
		return 0;
108
	}
109
	hdl->cpos = 0;
110
	hdl->rused = hdl->wused = 0;
111
	if (!sio_getpar(hdl, &hdl->par))
112
		return 0;
113
#ifdef DEBUG
114
	hdl->pollcnt = 0;
115
	clock_gettime(CLOCK_MONOTONIC, &ts);
116
	hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec;
117
#endif
118
	hdl->rdrop = hdl->wsil = 0;
119
	if (!hdl->ops->start(hdl))
120
		return 0;
121
	hdl->started = 1;
122
	return 1;
123
}
124
125
int
126
sio_stop(struct sio_hdl *hdl)
127
{
128
	if (hdl->eof) {
129
		DPRINTF("sio_stop: eof\n");
130
		return 0;
131
	}
132
	if (!hdl->started) {
133
		DPRINTF("sio_stop: not started\n");
134
		hdl->eof = 1;
135
		return 0;
136
	}
137
	if (!hdl->ops->stop(hdl))
138
		return 0;
139
#ifdef DEBUG
140
	DPRINTFN(2, "libsndio: polls: %llu, samples = %llu\n",
141
	    hdl->pollcnt, hdl->cpos);
142
#endif
143
	hdl->started = 0;
144
	return 1;
145
}
146
147
int
148
sio_setpar(struct sio_hdl *hdl, struct sio_par *par)
149
{
150
	if (hdl->eof) {
151
		DPRINTF("sio_setpar: eof\n");
152
		return 0;
153
	}
154
	if (par->__magic != SIO_PAR_MAGIC) {
155
		DPRINTF("sio_setpar: uninitialized sio_par structure\n");
156
		hdl->eof = 1;
157
		return 0;
158
	}
159
	if (hdl->started) {
160
		DPRINTF("sio_setpar: already started\n");
161
		hdl->eof = 1;
162
		return 0;
163
	}
164
	if (par->bufsz != ~0U) {
165
		DPRINTF("sio_setpar: setting bufsz is deprecated\n");
166
		par->appbufsz = par->bufsz;
167
		par->bufsz = ~0U;
168
	}
169
	if (par->rate != ~0U && par->appbufsz == ~0U)
170
		par->appbufsz = par->rate * 200 / 1000;
171
	return hdl->ops->setpar(hdl, par);
172
}
173
174
int
175
sio_getpar(struct sio_hdl *hdl, struct sio_par *par)
176
{
177
	if (hdl->eof) {
178
		DPRINTF("sio_getpar: eof\n");
179
		return 0;
180
	}
181
	if (hdl->started) {
182
		DPRINTF("sio_getpar: already started\n");
183
		hdl->eof = 1;
184
		return 0;
185
	}
186
	if (!hdl->ops->getpar(hdl, par)) {
187
		par->__magic = 0;
188
		return 0;
189
	}
190
	par->__magic = 0;
191
	return 1;
192
}
193
194
int
195
sio_getcap(struct sio_hdl *hdl, struct sio_cap *cap)
196
{
197
	if (hdl->eof) {
198
		DPRINTF("sio_getcap: eof\n");
199
		return 0;
200
	}
201
	if (hdl->started) {
202
		DPRINTF("sio_getcap: already started\n");
203
		hdl->eof = 1;
204
		return 0;
205
	}
206
	return hdl->ops->getcap(hdl, cap);
207
}
208
209
static int
210
sio_psleep(struct sio_hdl *hdl, int event)
211
{
212
	struct pollfd pfd[SIO_MAXNFDS];
213
	int revents;
214
	int nfds;
215
216
	nfds = sio_nfds(hdl);
217
	if (nfds > SIO_MAXNFDS) {
218
		DPRINTF("sio_psleep: %d: too many descriptors\n", nfds);
219
		hdl->eof = 1;
220
		return 0;
221
	}
222
	for (;;) {
223
		nfds = sio_pollfd(hdl, pfd, event);
224
		while (poll(pfd, nfds, -1) < 0) {
225
			if (errno == EINTR)
226
				continue;
227
			DPERROR("sio_psleep: poll");
228
			hdl->eof = 1;
229
			return 0;
230
		}
231
		revents = sio_revents(hdl, pfd);
232
		if (revents & POLLHUP) {
233
			DPRINTF("sio_psleep: hang-up\n");
234
			return 0;
235
		}
236
		if (revents & event)
237
			break;
238
	}
239
	return 1;
240
}
241
242
static int
243
sio_rdrop(struct sio_hdl *hdl)
244
{
245
#define DROP_NMAX 0x1000
246
	static char dummy[DROP_NMAX];
247
	ssize_t n, todo;
248
249
	while (hdl->rdrop > 0) {
250
		todo = hdl->rdrop;
251
		if (todo > DROP_NMAX)
252
			todo = DROP_NMAX;
253
		n = hdl->ops->read(hdl, dummy, todo);
254
		if (n == 0)
255
			return 0;
256
		hdl->rdrop -= n;
257
		DPRINTF("sio_rdrop: dropped %zu bytes\n", n);
258
	}
259
	return 1;
260
}
261
262
static int
263
sio_wsil(struct sio_hdl *hdl)
264
{
265
#define ZERO_NMAX 0x1000
266
	static char zero[ZERO_NMAX];
267
	ssize_t n, todo;
268
269
	while (hdl->wsil > 0) {
270
		todo = hdl->wsil;
271
		if (todo > ZERO_NMAX)
272
			todo = ZERO_NMAX;
273
		n = hdl->ops->write(hdl, zero, todo);
274
		if (n == 0)
275
			return 0;
276
		hdl->wsil -= n;
277
		DPRINTF("sio_wsil: inserted %zu bytes\n", n);
278
	}
279
	return 1;
280
}
281
282
size_t
283
sio_read(struct sio_hdl *hdl, void *buf, size_t len)
284
{
285
	unsigned int n;
286
	char *data = buf;
287
	size_t todo = len, maxread;
288
289
	if (hdl->eof) {
290
		DPRINTF("sio_read: eof\n");
291
		return 0;
292
	}
293
	if (!hdl->started || !(hdl->mode & SIO_REC)) {
294
		DPRINTF("sio_read: recording not started\n");
295
		hdl->eof = 1;
296
		return 0;
297
	}
298
	if (todo == 0) {
299
		DPRINTF("sio_read: zero length read ignored\n");
300
		return 0;
301
	}
302
	while (todo > 0) {
303
		if (!sio_rdrop(hdl))
304
			return 0;
305
		maxread = hdl->rused;
306
		if (maxread > todo)
307
			maxread = todo;
308
		n = maxread > 0 ? hdl->ops->read(hdl, data, maxread) : 0;
309
		if (n == 0) {
310
			if (hdl->nbio || hdl->eof || todo < len)
311
				break;
312
			if (!sio_psleep(hdl, POLLIN))
313
				break;
314
			continue;
315
		}
316
		data += n;
317
		todo -= n;
318
		hdl->rused -= n;
319
	}
320
	return len - todo;
321
}
322
323
size_t
324
sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
325
{
326
	unsigned int n;
327
	const unsigned char *data = buf;
328
	size_t todo = len, maxwrite;
329
330
	if (hdl->eof) {
331
		DPRINTF("sio_write: eof\n");
332
		return 0;
333
	}
334
	if (!hdl->started || !(hdl->mode & SIO_PLAY)) {
335
		DPRINTF("sio_write: playback not started\n");
336
		hdl->eof = 1;
337
		return 0;
338
	}
339
	if (todo == 0) {
340
		DPRINTF("sio_write: zero length write ignored\n");
341
		return 0;
342
	}
343
	while (todo > 0) {
344
		if (!sio_wsil(hdl))
345
			return 0;
346
		maxwrite = hdl->par.bufsz * hdl->par.pchan * hdl->par.bps -
347
		    hdl->wused;
348
		if (maxwrite > todo)
349
			maxwrite = todo;
350
		n = maxwrite > 0 ? hdl->ops->write(hdl, data, maxwrite) : 0;
351
		if (n == 0) {
352
			if (hdl->nbio || hdl->eof)
353
				break;
354
			if (!sio_psleep(hdl, POLLOUT))
355
				break;
356
			continue;
357
		}
358
		data += n;
359
		todo -= n;
360
		hdl->wused += n;
361
	}
362
	return len - todo;
363
}
364
365
int
366
sio_nfds(struct sio_hdl *hdl)
367
{
368
	return hdl->ops->nfds(hdl);
369
}
370
371
int
372
sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events)
373
{
374
	if (hdl->eof)
375
		return 0;
376
	if (!hdl->started)
377
		events = 0;
378
	return hdl->ops->pollfd(hdl, pfd, events);
379
}
380
381
int
382
sio_revents(struct sio_hdl *hdl, struct pollfd *pfd)
383
{
384
	int revents;
385
#ifdef DEBUG
386
	struct timespec ts0, ts1;
387
388
	if (_sndio_debug >= 4)
389
		clock_gettime(CLOCK_MONOTONIC, &ts0);
390
#endif
391
	if (hdl->eof)
392
		return POLLHUP;
393
#ifdef DEBUG
394
	hdl->pollcnt++;
395
#endif
396
	revents = hdl->ops->revents(hdl, pfd);
397
	if (!hdl->started)
398
		return revents & POLLHUP;
399
#ifdef DEBUG
400
	if (_sndio_debug >= 4) {
401
		clock_gettime(CLOCK_MONOTONIC, &ts1);
402
		DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n",
403
		    1000000000LL * ts0.tv_sec +
404
		    ts0.tv_nsec - hdl->start_nsec,
405
		    revents,
406
		    1000000000LL * (ts1.tv_sec - ts0.tv_sec) +
407
		    ts1.tv_nsec - ts0.tv_nsec);
408
	}
409
#endif
410
	if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl))
411
		revents &= ~POLLOUT;
412
	if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl))
413
		revents &= ~POLLIN;
414
	return revents;
415
}
416
417
int
418
sio_eof(struct sio_hdl *hdl)
419
{
420
	return hdl->eof;
421
}
422
423
void
424
sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr)
425
{
426
	if (hdl->started) {
427
		DPRINTF("sio_onmove: already started\n");
428
		hdl->eof = 1;
429
		return;
430
	}
431
	hdl->move_cb = cb;
432
	hdl->move_addr = addr;
433
}
434
435
#ifdef DEBUG
436
void
437
_sio_printpos(struct sio_hdl *hdl)
438
{
439
	struct timespec ts;
440
	long long rpos, rdiff;
441
	long long cpos, cdiff;
442
	long long wpos, wdiff;
443
	unsigned rbpf, wbpf, rround, wround;
444
445
	clock_gettime(CLOCK_MONOTONIC, &ts);
446
	rbpf = (hdl->mode & SIO_REC) ? hdl->par.bps * hdl->par.rchan : 1;
447
	wbpf = (hdl->mode & SIO_PLAY) ? hdl->par.bps * hdl->par.pchan : 1;
448
	rround = hdl->par.round * rbpf;
449
	wround = hdl->par.round * wbpf;
450
451
	rpos = (hdl->mode & SIO_REC) ?
452
	    hdl->cpos * rbpf - hdl->rused : 0;
453
	wpos = (hdl->mode & SIO_PLAY) ?
454
	    hdl->cpos * wbpf + hdl->wused : 0;
455
456
	cdiff = hdl->cpos % hdl->par.round;
457
	cpos  = hdl->cpos / hdl->par.round;
458
	if (cdiff > hdl->par.round / 2) {
459
		cpos++;
460
		cdiff = cdiff - hdl->par.round;
461
	}
462
	rdiff = rpos % rround;
463
	rpos  = rpos / rround;
464
	if (rdiff > rround / 2) {
465
		rpos++;
466
		rdiff = rdiff - rround;
467
	}
468
	wdiff = wpos % wround;
469
	wpos  = wpos / wround;
470
	if (wdiff > wround / 2) {
471
		wpos++;
472
		wdiff = wdiff - wround;
473
	}
474
	DPRINTF("%011lld: "
475
	    "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n",
476
	    1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec,
477
	    cpos, cdiff, wpos, wdiff, rpos, rdiff);
478
}
479
#endif
480
481
void
482
_sio_onmove_cb(struct sio_hdl *hdl, int delta)
483
{
484
	hdl->cpos += delta;
485
	if (hdl->mode & SIO_REC)
486
		hdl->rused += delta * (hdl->par.bps * hdl->par.rchan);
487
	if (hdl->mode & SIO_PLAY)
488
		hdl->wused -= delta * (hdl->par.bps * hdl->par.pchan);
489
#ifdef DEBUG
490
	if (_sndio_debug >= 3)
491
		_sio_printpos(hdl);
492
	if ((hdl->mode & SIO_PLAY) && hdl->wused < 0) {
493
		DPRINTFN(1, "sndio: h/w failure: negative buffer usage\n");
494
		hdl->eof = 1;
495
		return;
496
	}
497
#endif
498
	if (hdl->move_cb)
499
		hdl->move_cb(hdl->move_addr, delta);
500
}
501
502
int
503
sio_setvol(struct sio_hdl *hdl, unsigned int ctl)
504
{
505
	if (hdl->eof)
506
		return 0;
507
	if (!hdl->ops->setvol)
508
		return 1;
509
	if (!hdl->ops->setvol(hdl, ctl))
510
		return 0;
511
	hdl->ops->getvol(hdl);
512
	return 1;
513
}
514
515
int
516
sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned int), void *addr)
517
{
518
	if (hdl->started) {
519
		DPRINTF("sio_onvol: already started\n");
520
		hdl->eof = 1;
521
		return 0;
522
	}
523
	if (!hdl->ops->setvol)
524
		return 0;
525
	hdl->vol_cb = cb;
526
	hdl->vol_addr = addr;
527
	hdl->ops->getvol(hdl);
528
	return 1;
529
}
530
531
void
532
_sio_onvol_cb(struct sio_hdl *hdl, unsigned int ctl)
533
{
534
	if (hdl->vol_cb)
535
		hdl->vol_cb(hdl->vol_addr, ctl);
536
}