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