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