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