xref: /openbsd-src/lib/libsndio/sio.c (revision 20fb3e8c67df45ca4633b11b5eeeb8cf162b4d80)
1 /*	$OpenBSD: sio.c,v 1.6 2011/05/09 17:34:14 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/time.h>
21 #include <sys/stat.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 
34 #define SIO_PAR_MAGIC	0x83b905a4
35 
36 struct sio_backend {
37 	char *prefix;
38 	struct sio_hdl *(*open)(const char *, unsigned, int);
39 };
40 
41 static struct sio_backend backends[] = {
42 	{ "aucat", sio_aucat_open },
43 	{ "sun", sio_sun_open },
44 	{ NULL, NULL }
45 };
46 
47 void
48 sio_initpar(struct sio_par *par)
49 {
50 	memset(par, 0xff, sizeof(struct sio_par));
51 	par->__magic = SIO_PAR_MAGIC;
52 }
53 
54 struct sio_hdl *
55 sio_open(const char *str, unsigned mode, int nbio)
56 {
57 	struct sio_backend *b;
58 	struct sio_hdl *hdl;
59 	char *sep;
60 	int len;
61 
62 #ifdef DEBUG
63 	sndio_debug_init();
64 #endif
65 	if ((mode & (SIO_PLAY | SIO_REC)) == 0)
66 		return NULL;
67 	if (str == NULL && !issetugid())
68 		str = getenv("AUDIODEVICE");
69 	if (str == NULL) {
70 		for (b = backends; b->prefix != NULL; b++) {
71 			hdl = b->open(NULL, mode, nbio);
72 			if (hdl != NULL)
73 				return hdl;
74 		}
75 		return NULL;
76 	}
77 	sep = strchr(str, ':');
78 	if (sep == NULL) {
79 		DPRINTF("sio_open: %s: ':' missing in device name\n", str);
80 		return NULL;
81 	}
82 	len = sep - str;
83 	for (b = backends; b->prefix != NULL; b++) {
84 		if (strlen(b->prefix) == len && memcmp(b->prefix, str, len) == 0)
85 			return b->open(sep + 1, mode, nbio);
86 	}
87 	DPRINTF("sio_open: %s: unknown device type\n", str);
88 	return NULL;
89 }
90 
91 void
92 sio_create(struct sio_hdl *hdl, struct sio_ops *ops, unsigned mode, int nbio)
93 {
94 	hdl->ops = ops;
95 	hdl->mode = mode;
96 	hdl->nbio = nbio;
97 	hdl->started = 0;
98 	hdl->eof = 0;
99 	hdl->move_cb = NULL;
100 	hdl->vol_cb = NULL;
101 }
102 
103 void
104 sio_close(struct sio_hdl *hdl)
105 {
106 	hdl->ops->close(hdl);
107 }
108 
109 int
110 sio_start(struct sio_hdl *hdl)
111 {
112 	if (hdl->eof) {
113 		DPRINTF("sio_start: eof\n");
114 		return 0;
115 	}
116 	if (hdl->started) {
117 		DPRINTF("sio_start: already started\n");
118 		hdl->eof = 1;
119 		return 0;
120 	}
121 #ifdef DEBUG
122 	if (!sio_getpar(hdl, &hdl->par))
123 		return 0;
124 	hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->realpos = 0;
125 	gettimeofday(&hdl->tv, NULL);
126 #endif
127 	if (!hdl->ops->start(hdl))
128 		return 0;
129 	hdl->started = 1;
130 	return 1;
131 }
132 
133 int
134 sio_stop(struct sio_hdl *hdl)
135 {
136 	if (hdl->eof) {
137 		DPRINTF("sio_stop: eof\n");
138 		return 0;
139 	}
140 	if (!hdl->started) {
141 		DPRINTF("sio_stop: not started\n");
142 		hdl->eof = 1;
143 		return 0;
144 	}
145 	if (!hdl->ops->stop(hdl))
146 		return 0;
147 #ifdef DEBUG
148 	DPRINTF("libsndio: polls: %llu, written = %llu, read: %llu\n",
149 	    hdl->pollcnt, hdl->wcnt, hdl->rcnt);
150 #endif
151 	hdl->started = 0;
152 	return 1;
153 }
154 
155 int
156 sio_setpar(struct sio_hdl *hdl, struct sio_par *par)
157 {
158 	if (hdl->eof) {
159 		DPRINTF("sio_setpar: eof\n");
160 		return 0;
161 	}
162 	if (par->__magic != SIO_PAR_MAGIC) {
163 		DPRINTF("sio_setpar: use of uninitialized sio_par structure\n");
164 		hdl->eof = 1;
165 		return 0;
166 	}
167 	if (hdl->started) {
168 		DPRINTF("sio_setpar: already started\n");
169 		hdl->eof = 1;
170 		return 0;
171 	}
172 	if (par->bufsz != ~0U) {
173 		DPRINTF("sio_setpar: setting bufsz is deprecated\n");
174 		par->appbufsz = par->bufsz;
175 	}
176 	if (par->rate != ~0U && par->appbufsz == ~0U)
177 		par->appbufsz = par->rate * 200 / 1000;
178 	return hdl->ops->setpar(hdl, par);
179 }
180 
181 int
182 sio_getpar(struct sio_hdl *hdl, struct sio_par *par)
183 {
184 	if (hdl->eof) {
185 		DPRINTF("sio_getpar: eof\n");
186 		return 0;
187 	}
188 	if (hdl->started) {
189 		DPRINTF("sio_getpar: already started\n");
190 		hdl->eof = 1;
191 		return 0;
192 	}
193 	if (!hdl->ops->getpar(hdl, par)) {
194 		par->__magic = 0;
195 		return 0;
196 	}
197 	par->__magic = 0;
198 	return 1;
199 }
200 
201 int
202 sio_getcap(struct sio_hdl *hdl, struct sio_cap *cap)
203 {
204 	if (hdl->eof) {
205 		DPRINTF("sio_getcap: eof\n");
206 		return 0;
207 	}
208 	if (hdl->started) {
209 		DPRINTF("sio_getcap: already started\n");
210 		hdl->eof = 1;
211 		return 0;
212 	}
213 	return hdl->ops->getcap(hdl, cap);
214 }
215 
216 static int
217 sio_psleep(struct sio_hdl *hdl, int event)
218 {
219 	struct pollfd pfd[SIO_MAXNFDS];
220 	int revents;
221 	nfds_t nfds;
222 
223 	nfds = sio_nfds(hdl);
224 	for (;;) {
225 		sio_pollfd(hdl, pfd, event);
226 		while (poll(pfd, nfds, -1) < 0) {
227 			if (errno == EINTR)
228 				continue;
229 			DPERROR("sio_psleep: poll");
230 			hdl->eof = 1;
231 			return 0;
232 		}
233 		revents = sio_revents(hdl, pfd);
234 		if (revents & POLLHUP) {
235 			DPRINTF("sio_psleep: hang-up\n");
236 			return 0;
237 		}
238 		if (revents & event)
239 			break;
240 	}
241 	return 1;
242 }
243 
244 size_t
245 sio_read(struct sio_hdl *hdl, void *buf, size_t len)
246 {
247 	unsigned n;
248 	char *data = buf;
249 	size_t todo = len;
250 
251 	if (hdl->eof) {
252 		DPRINTF("sio_read: eof\n");
253 		return 0;
254 	}
255 	if (!hdl->started || !(hdl->mode & SIO_REC)) {
256 		DPRINTF("sio_read: recording not started\n");
257 		hdl->eof = 1;
258 		return 0;
259 	}
260 	if (todo == 0) {
261 		DPRINTF("sio_read: zero length read ignored\n");
262 		return 0;
263 	}
264 	while (todo > 0) {
265 		n = hdl->ops->read(hdl, data, todo);
266 		if (n == 0) {
267 			if (hdl->nbio || hdl->eof || todo < len)
268 				break;
269 			if (!sio_psleep(hdl, POLLIN))
270 				break;
271 			continue;
272 		}
273 		data += n;
274 		todo -= n;
275 #ifdef DEBUG
276 		hdl->rcnt += n;
277 #endif
278 	}
279 	return len - todo;
280 }
281 
282 size_t
283 sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
284 {
285 	unsigned n;
286 	const unsigned char *data = buf;
287 	size_t todo = len;
288 #ifdef DEBUG
289 	struct timeval tv0, tv1, dtv;
290 	unsigned us;
291 
292 	if (sndio_debug >= 2)
293 		gettimeofday(&tv0, NULL);
294 #endif
295 
296 	if (hdl->eof) {
297 		DPRINTF("sio_write: eof\n");
298 		return 0;
299 	}
300 	if (!hdl->started || !(hdl->mode & SIO_PLAY)) {
301 		DPRINTF("sio_write: playback not started\n");
302 		hdl->eof = 1;
303 		return 0;
304 	}
305 	if (todo == 0) {
306 		DPRINTF("sio_write: zero length write ignored\n");
307 		return 0;
308 	}
309 	while (todo > 0) {
310 		n = hdl->ops->write(hdl, data, todo);
311 		if (n == 0) {
312 			if (hdl->nbio || hdl->eof)
313 				break;
314 			if (!sio_psleep(hdl, POLLOUT))
315 				break;
316 			continue;
317 		}
318 		data += n;
319 		todo -= n;
320 #ifdef DEBUG
321 		hdl->wcnt += n;
322 #endif
323 	}
324 #ifdef DEBUG
325 	if (sndio_debug >= 2) {
326 		gettimeofday(&tv1, NULL);
327 		timersub(&tv0, &hdl->tv, &dtv);
328 		DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec);
329 
330 		timersub(&tv1, &tv0, &dtv);
331 		us = dtv.tv_sec * 1000000 + dtv.tv_usec;
332 		DPRINTF(
333 		    "sio_write: wrote %d bytes of %d in %uus\n",
334 		    (int)(len - todo), (int)len, us);
335 	}
336 #endif
337 	return len - todo;
338 }
339 
340 int
341 sio_nfds(struct sio_hdl *hdl)
342 {
343 	return hdl->ops->nfds(hdl);
344 }
345 
346 int
347 sio_pollfd(struct sio_hdl *hdl, struct pollfd *pfd, int events)
348 {
349 	if (hdl->eof)
350 		return 0;
351 	if (!hdl->started)
352 		events = 0;
353 	return hdl->ops->pollfd(hdl, pfd, events);
354 }
355 
356 int
357 sio_revents(struct sio_hdl *hdl, struct pollfd *pfd)
358 {
359 	int revents;
360 #ifdef DEBUG
361 	struct timeval tv0, tv1, dtv;
362 	unsigned us;
363 
364 	if (sndio_debug >= 2)
365 		gettimeofday(&tv0, NULL);
366 #endif
367 	if (hdl->eof)
368 		return POLLHUP;
369 #ifdef DEBUG
370 	hdl->pollcnt++;
371 #endif
372 	revents = hdl->ops->revents(hdl, pfd);
373 	if (!hdl->started)
374 		return revents & POLLHUP;
375 #ifdef DEBUG
376 	if (sndio_debug >= 2) {
377 		gettimeofday(&tv1, NULL);
378 		timersub(&tv0, &hdl->tv, &dtv);
379 		DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec);
380 
381 		timersub(&tv1, &tv0, &dtv);
382 		us = dtv.tv_sec * 1000000 + dtv.tv_usec;
383 		DPRINTF("sio_revents: revents = 0x%x, complete in %uus\n",
384 		    revents, us);
385 	}
386 #endif
387 	return revents;
388 }
389 
390 int
391 sio_eof(struct sio_hdl *hdl)
392 {
393 	return hdl->eof;
394 }
395 
396 void
397 sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr)
398 {
399 	if (hdl->started) {
400 		DPRINTF("sio_onmove: already started\n");
401 		hdl->eof = 1;
402 		return;
403 	}
404 	hdl->move_cb = cb;
405 	hdl->move_addr = addr;
406 }
407 
408 void
409 sio_onmove_cb(struct sio_hdl *hdl, int delta)
410 {
411 #ifdef DEBUG
412 	struct timeval tv0, dtv;
413 	long long playpos;
414 
415 	if (sndio_debug >= 3 && (hdl->mode & SIO_PLAY)) {
416 		gettimeofday(&tv0, NULL);
417 		timersub(&tv0, &hdl->tv, &dtv);
418 		DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec);
419 		hdl->realpos += delta;
420 		playpos = hdl->wcnt / (hdl->par.bps * hdl->par.pchan);
421 		DPRINTF("sio_onmove_cb: delta = %+7d, "
422 		    "plat = %+7lld, "
423 		    "realpos = %+7lld, "
424 		    "bufused = %+7lld\n",
425 		    delta,
426 		    playpos - hdl->realpos,
427 		    hdl->realpos,
428 		    hdl->realpos < 0 ? playpos : playpos - hdl->realpos);
429 	}
430 #endif
431 	if (hdl->move_cb)
432 		hdl->move_cb(hdl->move_addr, delta);
433 }
434 
435 int
436 sio_setvol(struct sio_hdl *hdl, unsigned ctl)
437 {
438 	if (hdl->eof)
439 		return 0;
440 	if (!hdl->ops->setvol)
441 		return 1;
442 	if (!hdl->ops->setvol(hdl, ctl))
443 		return 0;
444 	hdl->ops->getvol(hdl);
445 	return 1;
446 }
447 
448 int
449 sio_onvol(struct sio_hdl *hdl, void (*cb)(void *, unsigned), void *addr)
450 {
451 	if (hdl->started) {
452 		DPRINTF("sio_onvol: already started\n");
453 		hdl->eof = 1;
454 		return 0;
455 	}
456 	if (!hdl->ops->setvol)
457 		return 0;
458 	hdl->vol_cb = cb;
459 	hdl->vol_addr = addr;
460 	hdl->ops->getvol(hdl);
461 	return 1;
462 }
463 
464 void
465 sio_onvol_cb(struct sio_hdl *hdl, unsigned ctl)
466 {
467 	if (hdl->vol_cb)
468 		hdl->vol_cb(hdl->vol_addr, ctl);
469 }
470