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