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