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