xref: /openbsd-src/lib/libsndio/aucat.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: aucat.c,v 1.15 2009/02/25 23:31:59 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 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <poll.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 
28 #include "amsg.h"
29 #include "sndio_priv.h"
30 
31 struct aucat_hdl {
32 	struct sio_hdl sa;
33 	int fd;				/* socket */
34 	struct amsg rmsg, wmsg;		/* temporary messages */
35 	size_t wtodo, rtodo;		/* bytes to complete the packet */
36 #define STATE_IDLE	0		/* nothing to do */
37 #define STATE_MSG	1		/* message being transferred */
38 #define STATE_DATA	2		/* data being transferred */
39 	unsigned rstate, wstate;	/* one of above */
40 	unsigned rbpf, wbpf;		/* read and write bytes-per-frame */
41 	int maxwrite;			/* latency constraint */
42 	int events;			/* events the user requested */
43 	unsigned curvol, reqvol;	/* current and requested volume */
44 };
45 
46 static void aucat_close(struct sio_hdl *);
47 static int aucat_start(struct sio_hdl *);
48 static int aucat_stop(struct sio_hdl *);
49 static int aucat_setpar(struct sio_hdl *, struct sio_par *);
50 static int aucat_getpar(struct sio_hdl *, struct sio_par *);
51 static int aucat_getcap(struct sio_hdl *, struct sio_cap *);
52 static size_t aucat_read(struct sio_hdl *, void *, size_t);
53 static size_t aucat_write(struct sio_hdl *, void *, size_t);
54 static int aucat_pollfd(struct sio_hdl *, struct pollfd *, int);
55 static int aucat_revents(struct sio_hdl *, struct pollfd *);
56 static int aucat_setvol(struct sio_hdl *, unsigned);
57 static void aucat_getvol(struct sio_hdl *);
58 
59 static struct sio_ops aucat_ops = {
60 	aucat_close,
61 	aucat_setpar,
62 	aucat_getpar,
63 	aucat_getcap,
64 	aucat_write,
65 	aucat_read,
66 	aucat_start,
67 	aucat_stop,
68 	aucat_pollfd,
69 	aucat_revents,
70 	aucat_setvol,
71 	aucat_getvol
72 };
73 
74 struct sio_hdl *
75 sio_open_aucat(char *path, unsigned mode, int nbio)
76 {
77 	int s;
78 	struct sio_cap cap;
79 	struct aucat_hdl *hdl;
80 	struct sockaddr_un ca;
81 	socklen_t len = sizeof(struct sockaddr_un);
82 	uid_t uid;
83 
84 	if (path == NULL)
85 		path = SIO_AUCAT_PATH;
86 	uid = geteuid();
87 	if (strchr(path, '/') != NULL)
88 		return NULL;
89 	snprintf(ca.sun_path, sizeof(ca.sun_path),
90 	    "/tmp/aucat-%u/%s", uid, path);
91 	ca.sun_family = AF_UNIX;
92 
93 	hdl = malloc(sizeof(struct aucat_hdl));
94 	if (hdl == NULL)
95 		return NULL;
96 	sio_create(&hdl->sa, &aucat_ops, mode, nbio);
97 
98 	s = socket(AF_UNIX, SOCK_STREAM, 0);
99 	if (s < 0)
100 		goto bad_free;
101 	while (connect(s, (struct sockaddr *)&ca, len) < 0) {
102 		if (errno == EINTR)
103 			continue;
104 		goto bad_connect;
105 	}
106 	if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) {
107 		DPERROR(&hdl->sa, "FD_CLOEXEC");
108 		goto bad_connect;
109 	}
110 	hdl->fd = s;
111 	hdl->rstate = STATE_IDLE;
112 	hdl->rtodo = 0xdeadbeef;
113 	hdl->wstate = STATE_IDLE;
114 	hdl->wtodo = 0xdeadbeef;
115 	hdl->curvol = SIO_MAXVOL;
116 	hdl->reqvol = SIO_MAXVOL;
117 	if (!sio_getcap(&hdl->sa, &cap))
118 		goto bad_connect;
119 	if (((mode & SIO_PLAY) && cap.confs[0].pchan == 0) ||
120 	    ((mode & SIO_REC)  && cap.confs[0].rchan == 0))
121 		goto bad_connect;
122 	return (struct sio_hdl *)hdl;
123  bad_connect:
124 	while (close(s) < 0 && errno == EINTR)
125 		; /* retry */
126  bad_free:
127 	free(hdl);
128 	return NULL;
129 }
130 
131 /*
132  * read a message, return 0 if blocked
133  */
134 static int
135 aucat_rmsg(struct aucat_hdl *hdl)
136 {
137 	ssize_t n;
138 	unsigned char *data;
139 
140 	while (hdl->rtodo > 0) {
141 		data = (unsigned char *)&hdl->rmsg;
142 		data += sizeof(struct amsg) - hdl->rtodo;
143 		while ((n = read(hdl->fd, data, hdl->rtodo)) < 0) {
144 			if (errno == EINTR)
145 				continue;
146 			if (errno != EAGAIN) {
147 				hdl->sa.eof = 1;
148 				DPERROR(&hdl->sa, "aucat_rmsg: read");
149 			}
150 			return 0;
151 		}
152 		if (n == 0) {
153 			DPRINTF(&hdl->sa, "aucat_rmsg: eof\n");
154 			hdl->sa.eof = 1;
155 			return 0;
156 		}
157 		hdl->rtodo -= n;
158 	}
159 	return 1;
160 }
161 
162 /*
163  * write a message, return 0 if blocked
164  */
165 static int
166 aucat_wmsg(struct aucat_hdl *hdl)
167 {
168 	ssize_t n;
169 	unsigned char *data;
170 
171 	while (hdl->wtodo > 0) {
172 		data = (unsigned char *)&hdl->wmsg;
173 		data += sizeof(struct amsg) - hdl->wtodo;
174 		while ((n = write(hdl->fd, data, hdl->wtodo)) < 0) {
175 			if (errno == EINTR)
176 				continue;
177 			if (errno != EAGAIN) {
178 				hdl->sa.eof = 1;
179 				DPERROR(&hdl->sa, "aucat_wmsg: write");
180 			}
181 			return 0;
182 		}
183 		hdl->wtodo -= n;
184 	}
185 	return 1;
186 }
187 
188 /*
189  * execute the next message, return 0 if blocked
190  */
191 static int
192 aucat_runmsg(struct aucat_hdl *hdl)
193 {
194 	if (!aucat_rmsg(hdl))
195 		return 0;
196 	switch (hdl->rmsg.cmd) {
197 	case AMSG_DATA:
198 		if (hdl->rmsg.u.data.size == 0 ||
199 		    hdl->rmsg.u.data.size % hdl->rbpf) {
200 			DPRINTF(&hdl->sa, "aucat_runmsg: bad data message\n");
201 			hdl->sa.eof = 1;
202 			return 0;
203 		}
204 		hdl->rstate = STATE_DATA;
205 		hdl->rtodo = hdl->rmsg.u.data.size;
206 		break;
207 	case AMSG_MOVE:
208 		hdl->maxwrite += hdl->rmsg.u.ts.delta * (int)hdl->wbpf;
209 		sio_onmove_cb(&hdl->sa, hdl->rmsg.u.ts.delta);
210 		hdl->rstate = STATE_MSG;
211 		hdl->rtodo = sizeof(struct amsg);
212 		break;
213 	case AMSG_GETPAR:
214 	case AMSG_ACK:
215 		hdl->rstate = STATE_IDLE;
216 		hdl->rtodo = 0xdeadbeef;
217 		break;
218 	default:
219 		DPRINTF(&hdl->sa, "aucat_runmsg: unknown message\n");
220 		hdl->sa.eof = 1;
221 		return 0;
222 	}
223 	return 1;
224 }
225 
226 static void
227 aucat_close(struct sio_hdl *sh)
228 {
229 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
230 
231 	while (close(hdl->fd) < 0 && errno == EINTR)
232 		; /* nothing */
233 	free(hdl);
234 }
235 
236 static int
237 aucat_start(struct sio_hdl *sh)
238 {
239 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
240 	struct sio_par par;
241 
242 	/*
243 	 * save bpf
244 	 */
245 	if (!sio_getpar(&hdl->sa, &par))
246 		return 0;
247 	hdl->wbpf = par.bps * par.pchan;
248 	hdl->rbpf = par.bps * par.rchan;
249 	hdl->maxwrite = hdl->wbpf * par.bufsz;
250 
251 	AMSG_INIT(&hdl->wmsg);
252 	hdl->wmsg.cmd = AMSG_START;
253 	hdl->wtodo = sizeof(struct amsg);
254 	if (!aucat_wmsg(hdl))
255 		return 0;
256 	hdl->rstate = STATE_MSG;
257 	hdl->rtodo = sizeof(struct amsg);
258 	if (fcntl(hdl->fd, F_SETFL, O_NONBLOCK) < 0) {
259 		DPERROR(&hdl->sa, "aucat_start: fcntl(0)");
260 		hdl->sa.eof = 1;
261 		return 0;
262 	}
263 	return 1;
264 }
265 
266 static int
267 aucat_stop(struct sio_hdl *sh)
268 {
269 #define ZERO_MAX 0x400
270 	static unsigned char zero[ZERO_MAX];
271 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
272 	unsigned n, count, todo;
273 
274 	if (fcntl(hdl->fd, F_SETFL, 0) < 0) {
275 		DPERROR(&hdl->sa, "aucat_stop: fcntl(0)");
276 		hdl->sa.eof = 1;
277 		return 0;
278 	}
279 
280 	/*
281 	 * complete data block in progress
282 	 */
283 	if (hdl->wstate != STATE_IDLE) {
284 		todo = (hdl->wstate == STATE_MSG) ?
285 		    hdl->wmsg.u.data.size : hdl->wtodo;
286 		hdl->maxwrite = todo;
287 		memset(zero, 0, ZERO_MAX);
288 		while (todo > 0) {
289 			count = todo;
290 			if (count > ZERO_MAX)
291 				count = ZERO_MAX;
292 			n = aucat_write(&hdl->sa, zero, count);
293 			if (n == 0)
294 				return 0;
295 			todo -= n;
296 		}
297 	}
298 
299 	/*
300 	 * send stop message
301 	 */
302 	AMSG_INIT(&hdl->wmsg);
303 	hdl->wmsg.cmd = AMSG_STOP;
304 	hdl->wtodo = sizeof(struct amsg);
305 	if (!aucat_wmsg(hdl))
306 		return 0;
307 	if (hdl->rstate == STATE_IDLE) {
308 		hdl->rstate = STATE_MSG;
309 		hdl->rtodo = sizeof(struct amsg);
310 	}
311 
312 	/*
313 	 * wait for the STOP ACK
314 	 */
315 	while (hdl->rstate != STATE_IDLE) {
316 		switch (hdl->rstate) {
317 		case STATE_MSG:
318 			if (!aucat_runmsg(hdl))
319 				return 0;
320 			break;
321 		case STATE_DATA:
322 			if (!aucat_read(&hdl->sa, zero, ZERO_MAX))
323 				return 0;
324 			break;
325 		}
326 	}
327 	return 1;
328 }
329 
330 static int
331 aucat_setpar(struct sio_hdl *sh, struct sio_par *par)
332 {
333 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
334 
335 	AMSG_INIT(&hdl->wmsg);
336 	hdl->wmsg.cmd = AMSG_SETPAR;
337 	hdl->wmsg.u.par.bits = par->bits;
338 	hdl->wmsg.u.par.bps = par->bps;
339 	hdl->wmsg.u.par.sig = par->sig;
340 	hdl->wmsg.u.par.le = par->le;
341 	hdl->wmsg.u.par.msb = par->msb;
342 	hdl->wmsg.u.par.rate = par->rate;
343 	hdl->wmsg.u.par.appbufsz = par->appbufsz;
344 	hdl->wmsg.u.par.xrun = par->xrun;
345 	hdl->wmsg.u.par.mode = hdl->sa.mode;
346 	if (hdl->sa.mode & SIO_REC)
347 		hdl->wmsg.u.par.rchan = par->rchan;
348 	if (hdl->sa.mode & SIO_PLAY)
349 		hdl->wmsg.u.par.pchan = par->pchan;
350 	hdl->wtodo = sizeof(struct amsg);
351 	if (!aucat_wmsg(hdl))
352 		return 0;
353 	return 1;
354 }
355 
356 static int
357 aucat_getpar(struct sio_hdl *sh, struct sio_par *par)
358 {
359 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
360 
361 	AMSG_INIT(&hdl->rmsg);
362 	hdl->wmsg.cmd = AMSG_GETPAR;
363 	hdl->wtodo = sizeof(struct amsg);
364 	if (!aucat_wmsg(hdl))
365 		return 0;
366 	hdl->rtodo = sizeof(struct amsg);
367 	if (!aucat_rmsg(hdl))
368 		return 0;
369 	if (hdl->rmsg.cmd != AMSG_GETPAR) {
370 		DPRINTF(&hdl->sa, "aucat_getpar: protocol err\n");
371 		hdl->sa.eof = 1;
372 		return 0;
373 	}
374 	par->bits = hdl->rmsg.u.par.bits;
375 	par->bps = hdl->rmsg.u.par.bps;
376 	par->sig = hdl->rmsg.u.par.sig;
377 	par->le = hdl->rmsg.u.par.le;
378 	par->msb = hdl->rmsg.u.par.msb;
379 	par->rate = hdl->rmsg.u.par.rate;
380 	par->bufsz = hdl->rmsg.u.par.bufsz;
381 	par->appbufsz = hdl->rmsg.u.par.appbufsz;
382 	par->xrun = hdl->rmsg.u.par.xrun;
383 	par->round = hdl->rmsg.u.par.round;
384 	if (hdl->sa.mode & SIO_PLAY)
385 		par->pchan = hdl->rmsg.u.par.pchan;
386 	if (hdl->sa.mode & SIO_REC)
387 		par->rchan = hdl->rmsg.u.par.rchan;
388 	return 1;
389 }
390 
391 static int
392 aucat_getcap(struct sio_hdl *sh, struct sio_cap *cap)
393 {
394 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
395 
396 	AMSG_INIT(&hdl->rmsg);
397 	hdl->wmsg.cmd = AMSG_GETCAP;
398 	hdl->wtodo = sizeof(struct amsg);
399 	if (!aucat_wmsg(hdl))
400 		return 0;
401 	hdl->rtodo = sizeof(struct amsg);
402 	if (!aucat_rmsg(hdl))
403 		return 0;
404 	if (hdl->rmsg.cmd != AMSG_GETCAP) {
405 		DPRINTF(&hdl->sa, "aucat_getcap: protocol err\n");
406 		hdl->sa.eof = 1;
407 		return 0;
408 	}
409 	cap->enc[0].bits = hdl->rmsg.u.cap.bits;
410 	cap->enc[0].bps = SIO_BPS(hdl->rmsg.u.cap.bits);
411 	cap->enc[0].sig = 1;
412 	cap->enc[0].le = SIO_LE_NATIVE;
413 	cap->enc[0].msb = 1;
414 	cap->rchan[0] = hdl->rmsg.u.cap.rchan;
415 	cap->pchan[0] = hdl->rmsg.u.cap.pchan;
416 	cap->rate[0] = hdl->rmsg.u.cap.rate;
417 	cap->confs[0].enc = 1;
418 	cap->confs[0].rchan = (hdl->rmsg.u.cap.rchan > 0) ? 1 : 0;
419 	cap->confs[0].pchan = (hdl->rmsg.u.cap.pchan > 0) ? 1 : 0;
420 	cap->confs[0].rate = 1;
421 	cap->nconf = 1;
422 	return 1;
423 }
424 
425 static size_t
426 aucat_read(struct sio_hdl *sh, void *buf, size_t len)
427 {
428 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
429 	ssize_t n;
430 
431 	while (hdl->rstate != STATE_DATA) {
432 		switch (hdl->rstate) {
433 		case STATE_MSG:
434 			if (!aucat_runmsg(hdl))
435 				return 0;
436 			break;
437 		case STATE_IDLE:
438 			DPRINTF(&hdl->sa, "aucat_read: unexpected idle\n");
439 			break;
440 		}
441 	}
442 	if (len > hdl->rtodo)
443 		len = hdl->rtodo;
444 	while ((n = read(hdl->fd, buf, len)) < 0) {
445 		if (errno == EINTR)
446 			continue;
447 		if (errno != EAGAIN) {
448 			hdl->sa.eof = 1;
449 			DPERROR(&hdl->sa, "aucat_read: read");
450 		}
451 		return 0;
452 	}
453 	if (n == 0) {
454 		DPRINTF(&hdl->sa, "aucat_read: eof\n");
455 		hdl->sa.eof = 1;
456 		return 0;
457 	}
458 	hdl->rtodo -= n;
459 	if (hdl->rtodo == 0) {
460 		hdl->rstate = STATE_MSG;
461 		hdl->rtodo = sizeof(struct amsg);
462 	}
463 	return n;
464 }
465 
466 static int
467 aucat_buildmsg(struct aucat_hdl *hdl, size_t len)
468 {
469 	unsigned sz;
470 
471 	if (hdl->curvol != hdl->reqvol) {
472 		hdl->wstate = STATE_MSG;
473 		hdl->wtodo = sizeof(struct amsg);
474 		hdl->wmsg.cmd = AMSG_SETVOL;
475 		hdl->wmsg.u.vol.ctl = hdl->reqvol;
476 		hdl->curvol = hdl->reqvol;
477 		return 1;
478 	} else if (len > 0) {
479 		sz = (len < AMSG_DATAMAX) ? len : AMSG_DATAMAX;
480 		sz -= sz % hdl->wbpf;
481 		if (sz == 0)
482 			sz = hdl->wbpf;
483 		hdl->wstate = STATE_MSG;
484 		hdl->wtodo = sizeof(struct amsg);
485 		hdl->wmsg.cmd = AMSG_DATA;
486 		hdl->wmsg.u.data.size = sz;
487 		return 1;
488 	}
489 	return 0;
490 }
491 
492 static size_t
493 aucat_write(struct sio_hdl *sh, void *buf, size_t len)
494 {
495 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
496 	ssize_t n;
497 
498 	while (hdl->wstate != STATE_DATA) {
499 		switch (hdl->wstate) {
500 		case STATE_IDLE:
501 			if (!aucat_buildmsg(hdl, len))
502 				return 0;
503 			/* PASSTHROUGH */
504 		case STATE_MSG:
505 			if (!aucat_wmsg(hdl))
506 				return 0;
507 			if (hdl->wmsg.cmd == AMSG_DATA) {
508 				hdl->wstate = STATE_DATA;
509 				hdl->wtodo = hdl->wmsg.u.data.size;
510 			} else
511 				hdl->wstate = STATE_IDLE;
512 			break;
513 		default:
514 			DPRINTF(&hdl->sa, "aucat_write: bad state\n");
515 			abort();
516 		}
517 	}
518 	if (hdl->maxwrite <= 0)
519 		return 0;
520 	if (len > hdl->maxwrite)
521 		len = hdl->maxwrite;
522 	if (len > hdl->wtodo)
523 		len = hdl->wtodo;
524 	if (len == 0) {
525 		DPRINTF(&hdl->sa, "aucat_write: len == 0\n");
526 		abort();
527 	}
528 	while ((n = write(hdl->fd, buf, len)) < 0) {
529 		if (errno == EINTR)
530 			continue;
531 		if (errno != EAGAIN) {
532 			hdl->sa.eof = 1;
533 			DPERROR(&hdl->sa, "aucat_write: write");
534 		}
535 		return 0;
536 	}
537 	hdl->maxwrite -= n;
538 	hdl->wtodo -= n;
539 	if (hdl->wtodo == 0) {
540 		hdl->wstate = STATE_IDLE;
541 		hdl->wtodo = 0xdeadbeef;
542 	}
543 	return n;
544 }
545 
546 static int
547 aucat_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
548 {
549 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
550 
551 	hdl->events = events;
552 	if (hdl->maxwrite <= 0)
553 		events &= ~POLLOUT;
554 	if (hdl->rstate == STATE_MSG)
555 		events |= POLLIN;
556 	pfd->fd = hdl->fd;
557 	pfd->events = events;
558 	return 1;
559 }
560 
561 static int
562 aucat_revents(struct sio_hdl *sh, struct pollfd *pfd)
563 {
564 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
565 	int revents = pfd->revents;
566 
567 	if (revents & POLLIN) {
568 		while (hdl->rstate == STATE_MSG) {
569 			if (!aucat_runmsg(hdl)) {
570 				revents &= ~POLLIN;
571 				break;
572 			}
573 		}
574 	}
575 	if (revents & POLLOUT) {
576 		if (hdl->maxwrite <= 0)
577 			revents &= ~POLLOUT;
578 	}
579 	if (hdl->sa.eof)
580 		return POLLHUP;
581 	return revents & (hdl->events | POLLHUP);
582 }
583 
584 static int
585 aucat_setvol(struct sio_hdl *sh, unsigned vol)
586 {
587 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
588 
589 	hdl->reqvol = vol;
590 	return 1;
591 }
592 
593 static void
594 aucat_getvol(struct sio_hdl *sh)
595 {
596 	struct aucat_hdl *hdl = (struct aucat_hdl *)sh;
597 
598 	sio_onvol_cb(&hdl->sa, hdl->reqvol);
599 	return;
600 }
601