xref: /openbsd-src/usr.bin/sndiod/sock.c (revision 0cdbd9642b1957f38992284c46247b1985e88815)
1*0cdbd964Sratchov /*	$OpenBSD: sock.c,v 1.53 2024/12/21 08:57:18 ratchov Exp $	*/
287bc9f6aSratchov /*
387bc9f6aSratchov  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
487bc9f6aSratchov  *
587bc9f6aSratchov  * Permission to use, copy, modify, and distribute this software for any
687bc9f6aSratchov  * purpose with or without fee is hereby granted, provided that the above
787bc9f6aSratchov  * copyright notice and this permission notice appear in all copies.
887bc9f6aSratchov  *
987bc9f6aSratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1087bc9f6aSratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1187bc9f6aSratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1287bc9f6aSratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1387bc9f6aSratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1487bc9f6aSratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1587bc9f6aSratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1687bc9f6aSratchov  */
1787bc9f6aSratchov #include <sys/types.h>
1872ca3e97Sratchov #include <sys/socket.h>
1987bc9f6aSratchov #include <netinet/in.h>
2087bc9f6aSratchov #include <errno.h>
2187bc9f6aSratchov #include <poll.h>
2287bc9f6aSratchov #include <stdio.h>
2387bc9f6aSratchov #include <stdlib.h>
2487bc9f6aSratchov #include <string.h>
2587bc9f6aSratchov #include <unistd.h>
2687bc9f6aSratchov 
2787bc9f6aSratchov #include "abuf.h"
2887bc9f6aSratchov #include "defs.h"
2987bc9f6aSratchov #include "dev.h"
3087bc9f6aSratchov #include "file.h"
3187bc9f6aSratchov #include "midi.h"
3287bc9f6aSratchov #include "opt.h"
3387bc9f6aSratchov #include "sock.h"
3487bc9f6aSratchov #include "utils.h"
3587bc9f6aSratchov 
363e5ee6d4Sratchov #define SOCK_CTLDESC_SIZE	0x800	/* size of s->ctldesc */
37d07fece6Sratchov 
38fcda7a7eSratchov void sock_close(struct sock *);
39fcda7a7eSratchov void sock_slot_fill(void *);
40fcda7a7eSratchov void sock_slot_flush(void *);
41fcda7a7eSratchov void sock_slot_eof(void *);
426f413563Sratchov void sock_slot_onmove(void *);
434182f7f9Sratchov void sock_slot_onvol(void *);
44fcda7a7eSratchov void sock_midi_imsg(void *, unsigned char *, int);
45fcda7a7eSratchov void sock_midi_omsg(void *, unsigned char *, int);
46fcda7a7eSratchov void sock_midi_fill(void *, int);
47e575fbdeSratchov void sock_ctl_sync(void *);
48fcda7a7eSratchov struct sock *sock_new(int);
49fcda7a7eSratchov void sock_exit(void *);
50fcda7a7eSratchov int sock_fdwrite(struct sock *, void *, int);
51fcda7a7eSratchov int sock_fdread(struct sock *, void *, int);
52fcda7a7eSratchov int sock_rmsg(struct sock *);
53fcda7a7eSratchov int sock_wmsg(struct sock *);
54fcda7a7eSratchov int sock_rdata(struct sock *);
55fcda7a7eSratchov int sock_wdata(struct sock *);
56fcda7a7eSratchov int sock_setpar(struct sock *);
57fcda7a7eSratchov int sock_auth(struct sock *);
58fcda7a7eSratchov int sock_hello(struct sock *);
5987bc9f6aSratchov int sock_execmsg(struct sock *);
6087bc9f6aSratchov int sock_buildmsg(struct sock *);
61fcda7a7eSratchov int sock_read(struct sock *);
62fcda7a7eSratchov int sock_write(struct sock *);
6387bc9f6aSratchov int sock_pollfd(void *, struct pollfd *);
6487bc9f6aSratchov int sock_revents(void *, struct pollfd *);
6587bc9f6aSratchov void sock_in(void *);
6687bc9f6aSratchov void sock_out(void *);
6787bc9f6aSratchov void sock_hup(void *);
6887bc9f6aSratchov 
6987bc9f6aSratchov struct fileops sock_fileops = {
7087bc9f6aSratchov 	"sock",
7187bc9f6aSratchov 	sock_pollfd,
7287bc9f6aSratchov 	sock_revents,
7387bc9f6aSratchov 	sock_in,
7487bc9f6aSratchov 	sock_out,
7587bc9f6aSratchov 	sock_hup
7687bc9f6aSratchov };
7787bc9f6aSratchov 
7887bc9f6aSratchov struct slotops sock_slotops = {
7987bc9f6aSratchov 	sock_slot_onmove,
8087bc9f6aSratchov 	sock_slot_onvol,
8187bc9f6aSratchov 	sock_slot_fill,
8287bc9f6aSratchov 	sock_slot_flush,
8387bc9f6aSratchov 	sock_slot_eof,
8487bc9f6aSratchov 	sock_exit
8587bc9f6aSratchov };
8687bc9f6aSratchov 
8787bc9f6aSratchov struct midiops sock_midiops = {
8887bc9f6aSratchov 	sock_midi_imsg,
8987bc9f6aSratchov 	sock_midi_omsg,
9087bc9f6aSratchov 	sock_midi_fill,
9187bc9f6aSratchov 	sock_exit
9287bc9f6aSratchov };
9387bc9f6aSratchov 
94d07fece6Sratchov struct ctlops sock_ctlops = {
95e575fbdeSratchov 	sock_exit,
96e575fbdeSratchov 	sock_ctl_sync
97d07fece6Sratchov };
98d07fece6Sratchov 
9987bc9f6aSratchov struct sock *sock_list = NULL;
10087bc9f6aSratchov unsigned int sock_sesrefs = 0;		/* connections to the session */
10187bc9f6aSratchov uint8_t sock_sescookie[AMSG_COOKIELEN];	/* owner of the session */
10287bc9f6aSratchov 
10336355b88Sratchov /*
10436355b88Sratchov  * Old clients used to send dev number and opt name. This routine
10536355b88Sratchov  * finds proper opt pointer for the given device.
10636355b88Sratchov  */
10736355b88Sratchov static struct opt *
10836355b88Sratchov legacy_opt(int devnum, char *optname)
10936355b88Sratchov {
11036355b88Sratchov 	struct dev *d;
11136355b88Sratchov 	struct opt *o;
11236355b88Sratchov 
11336355b88Sratchov 	d = dev_bynum(devnum);
11436355b88Sratchov 	if (d == NULL)
11536355b88Sratchov 		return NULL;
11636355b88Sratchov 	if (strcmp(optname, "default") == 0) {
11736355b88Sratchov 		for (o = opt_list; o != NULL; o = o->next) {
11836355b88Sratchov 			if (strcmp(o->name, d->name) == 0)
11936355b88Sratchov 				return o;
12036355b88Sratchov 		}
12136355b88Sratchov 		return NULL;
12236355b88Sratchov 	} else {
12336355b88Sratchov 		o = opt_byname(optname);
12436355b88Sratchov 		return (o != NULL && o->dev == d) ? o : NULL;
12536355b88Sratchov 	}
12636355b88Sratchov }
12736355b88Sratchov 
12836355b88Sratchov /*
12936355b88Sratchov  * If control slot is associated to a particular opt, then
13036355b88Sratchov  * remove the unused group part of the control name to make mixer
13136355b88Sratchov  * look nicer
13236355b88Sratchov  */
13336355b88Sratchov static char *
13436355b88Sratchov ctlgroup(struct sock *f, struct ctl *c)
13536355b88Sratchov {
13636355b88Sratchov 	if (f->ctlslot->opt == NULL)
13736355b88Sratchov 		return c->group;
13836355b88Sratchov 	if (strcmp(c->group, f->ctlslot->opt->name) == 0)
13936355b88Sratchov 		return "";
14036355b88Sratchov 	if (strcmp(c->group, f->ctlslot->opt->dev->name) == 0)
14136355b88Sratchov 		return "";
14236355b88Sratchov 	return c->group;
14336355b88Sratchov }
14436355b88Sratchov 
14587bc9f6aSratchov void
14687bc9f6aSratchov sock_close(struct sock *f)
14787bc9f6aSratchov {
14836355b88Sratchov 	struct opt *o;
14987bc9f6aSratchov 	struct sock **pf;
150326545d4Sratchov 	unsigned int tags, i;
15187bc9f6aSratchov 
15287bc9f6aSratchov 	for (pf = &sock_list; *pf != f; pf = &(*pf)->next) {
15387bc9f6aSratchov #ifdef DEBUG
15487bc9f6aSratchov 		if (*pf == NULL) {
1557b639200Sratchov 			logx(0, "%s: not on list", __func__);
15687bc9f6aSratchov 			panic();
15787bc9f6aSratchov 		}
15887bc9f6aSratchov #endif
15987bc9f6aSratchov 	}
16087bc9f6aSratchov 	*pf = f->next;
16187bc9f6aSratchov 
16287bc9f6aSratchov #ifdef DEBUG
1637b639200Sratchov 	logx(3, "sock %d: closing", f->fd);
16487bc9f6aSratchov #endif
16587bc9f6aSratchov 	if (f->pstate > SOCK_AUTH)
16672ca3e97Sratchov 		sock_sesrefs -= f->sesrefs;
16787bc9f6aSratchov 	if (f->slot) {
16887bc9f6aSratchov 		slot_del(f->slot);
16987bc9f6aSratchov 		f->slot = NULL;
17087bc9f6aSratchov 	}
17187bc9f6aSratchov 	if (f->midi) {
172326545d4Sratchov 		tags = midi_tags(f->midi);
173326545d4Sratchov 		for (i = 0; i < DEV_NMAX; i++) {
17436355b88Sratchov 			if ((tags & (1 << i)) && (o = opt_bynum(i)) != NULL)
17536355b88Sratchov 				opt_unref(o);
176326545d4Sratchov 		}
17787bc9f6aSratchov 		midi_del(f->midi);
17887bc9f6aSratchov 		f->midi = NULL;
17987bc9f6aSratchov 	}
1809fd7fddfSratchov 	if (f->port) {
1819fd7fddfSratchov 		port_unref(f->port);
1829fd7fddfSratchov 		f->port = NULL;
1839fd7fddfSratchov 	}
184d07fece6Sratchov 	if (f->ctlslot) {
185d07fece6Sratchov 		ctlslot_del(f->ctlslot);
186d07fece6Sratchov 		f->ctlslot = NULL;
187d07fece6Sratchov 		xfree(f->ctldesc);
188d07fece6Sratchov 	}
18987bc9f6aSratchov 	file_del(f->file);
19087bc9f6aSratchov 	close(f->fd);
191e6f25aa0Sratchov 	file_slowaccept = 0;
19287bc9f6aSratchov 	xfree(f);
19387bc9f6aSratchov }
19487bc9f6aSratchov 
19587bc9f6aSratchov void
19687bc9f6aSratchov sock_slot_fill(void *arg)
19787bc9f6aSratchov {
19887bc9f6aSratchov 	struct sock *f = arg;
19987bc9f6aSratchov 	struct slot *s = f->slot;
20087bc9f6aSratchov 
20187bc9f6aSratchov 	f->fillpending += s->round;
20287bc9f6aSratchov #ifdef DEBUG
2037b639200Sratchov 	logx(4, "%s%u: fill, rmax -> %d, pending -> %d",
2047b639200Sratchov 	    s->name, s->unit, f->rmax, f->fillpending);
20587bc9f6aSratchov #endif
20687bc9f6aSratchov }
20787bc9f6aSratchov 
20887bc9f6aSratchov void
20987bc9f6aSratchov sock_slot_flush(void *arg)
21087bc9f6aSratchov {
21187bc9f6aSratchov 	struct sock *f = arg;
21287bc9f6aSratchov 	struct slot *s = f->slot;
21387bc9f6aSratchov 
21487bc9f6aSratchov 	f->wmax += s->round * s->sub.bpf;
21587bc9f6aSratchov #ifdef DEBUG
2167b639200Sratchov 	logx(4, "%s%u: flush, wmax -> %d", s->name, s->unit, f->wmax);
21787bc9f6aSratchov #endif
21887bc9f6aSratchov }
21987bc9f6aSratchov 
22087bc9f6aSratchov void
22187bc9f6aSratchov sock_slot_eof(void *arg)
22287bc9f6aSratchov {
22387bc9f6aSratchov 	struct sock *f = arg;
22487bc9f6aSratchov #ifdef DEBUG
2257b639200Sratchov 	struct slot *s = f->slot;
2267b639200Sratchov 
2277b639200Sratchov 	logx(3, "%s%u: eof", s->name, s->unit);
22887bc9f6aSratchov #endif
22987bc9f6aSratchov 	f->stoppending = 1;
23087bc9f6aSratchov }
23187bc9f6aSratchov 
23287bc9f6aSratchov void
2336f413563Sratchov sock_slot_onmove(void *arg)
23487bc9f6aSratchov {
23587bc9f6aSratchov 	struct sock *f = (struct sock *)arg;
23687bc9f6aSratchov 	struct slot *s = f->slot;
23787bc9f6aSratchov 
23887bc9f6aSratchov #ifdef DEBUG
2397b639200Sratchov 	logx(4, "%s%u: onmove: delta -> %d", s->name, s->unit, s->delta);
24087bc9f6aSratchov #endif
24187bc9f6aSratchov 	if (s->pstate != SOCK_START)
24287bc9f6aSratchov 		return;
24387bc9f6aSratchov 	f->tickpending++;
24487bc9f6aSratchov }
24587bc9f6aSratchov 
24687bc9f6aSratchov void
2474182f7f9Sratchov sock_slot_onvol(void *arg)
24887bc9f6aSratchov {
24987bc9f6aSratchov 	struct sock *f = (struct sock *)arg;
25087bc9f6aSratchov 	struct slot *s = f->slot;
25187bc9f6aSratchov 
25287bc9f6aSratchov #ifdef DEBUG
2537b639200Sratchov 	logx(4, "%s%u: onvol: vol -> %d", s->name, s->unit, s->vol);
25487bc9f6aSratchov #endif
25587bc9f6aSratchov 	if (s->pstate != SOCK_START)
25687bc9f6aSratchov 		return;
25787bc9f6aSratchov }
25887bc9f6aSratchov 
25987bc9f6aSratchov void
26087bc9f6aSratchov sock_midi_imsg(void *arg, unsigned char *msg, int size)
26187bc9f6aSratchov {
26287bc9f6aSratchov 	struct sock *f = arg;
26387bc9f6aSratchov 
26487bc9f6aSratchov 	midi_send(f->midi, msg, size);
26587bc9f6aSratchov }
26687bc9f6aSratchov 
26787bc9f6aSratchov void
26887bc9f6aSratchov sock_midi_omsg(void *arg, unsigned char *msg, int size)
26987bc9f6aSratchov {
27087bc9f6aSratchov 	struct sock *f = arg;
27187bc9f6aSratchov 
27287bc9f6aSratchov 	midi_out(f->midi, msg, size);
27387bc9f6aSratchov }
27487bc9f6aSratchov 
27587bc9f6aSratchov void
27687bc9f6aSratchov sock_midi_fill(void *arg, int count)
27787bc9f6aSratchov {
27887bc9f6aSratchov 	struct sock *f = arg;
27987bc9f6aSratchov 
28087bc9f6aSratchov 	f->fillpending += count;
28187bc9f6aSratchov }
28287bc9f6aSratchov 
283e575fbdeSratchov void
284e575fbdeSratchov sock_ctl_sync(void *arg)
285e575fbdeSratchov {
286e575fbdeSratchov 	struct sock *f = arg;
287e575fbdeSratchov 
288e575fbdeSratchov 	if (f->ctlops & SOCK_CTLDESC)
289e575fbdeSratchov 		f->ctlsyncpending = 1;
290e575fbdeSratchov }
291e575fbdeSratchov 
29287bc9f6aSratchov struct sock *
29387bc9f6aSratchov sock_new(int fd)
29487bc9f6aSratchov {
29587bc9f6aSratchov 	struct sock *f;
29687bc9f6aSratchov 
29787bc9f6aSratchov 	f = xmalloc(sizeof(struct sock));
29887bc9f6aSratchov 	f->pstate = SOCK_AUTH;
29987bc9f6aSratchov 	f->slot = NULL;
300fd35ec67Sratchov 	f->port = NULL;
30187bc9f6aSratchov 	f->midi = NULL;
302d07fece6Sratchov 	f->ctlslot = NULL;
30387bc9f6aSratchov 	f->tickpending = 0;
30487bc9f6aSratchov 	f->fillpending = 0;
30587bc9f6aSratchov 	f->stoppending = 0;
30687bc9f6aSratchov 	f->wstate = SOCK_WIDLE;
30787bc9f6aSratchov 	f->wtodo = 0xdeadbeef;
30887bc9f6aSratchov 	f->rstate = SOCK_RMSG;
30987bc9f6aSratchov 	f->rtodo = sizeof(struct amsg);
31087bc9f6aSratchov 	f->wmax = f->rmax = 0;
31187bc9f6aSratchov 	f->lastvol = -1;
312d07fece6Sratchov 	f->ctlops = 0;
313d07fece6Sratchov 	f->ctlsyncpending = 0;
31487bc9f6aSratchov 	f->file = file_new(&sock_fileops, f, "sock", 1);
31587bc9f6aSratchov 	f->fd = fd;
31687bc9f6aSratchov 	if (f->file == NULL) {
31787bc9f6aSratchov 		xfree(f);
31887bc9f6aSratchov 		return NULL;
31987bc9f6aSratchov 	}
32087bc9f6aSratchov 	f->next = sock_list;
32187bc9f6aSratchov 	sock_list = f;
32287bc9f6aSratchov 	return f;
32387bc9f6aSratchov }
32487bc9f6aSratchov 
32587bc9f6aSratchov void
32687bc9f6aSratchov sock_exit(void *arg)
32787bc9f6aSratchov {
32887bc9f6aSratchov 	struct sock *f = (struct sock *)arg;
32987bc9f6aSratchov 
33087bc9f6aSratchov #ifdef DEBUG
3317b639200Sratchov 	logx(3, "sock %d: exit", f->fd);
33287bc9f6aSratchov #endif
33387bc9f6aSratchov 	sock_close(f);
33487bc9f6aSratchov }
33587bc9f6aSratchov 
33687bc9f6aSratchov /*
3377c71888cSratchov  * write on the socket fd and handle errors
33887bc9f6aSratchov  */
33987bc9f6aSratchov int
34087bc9f6aSratchov sock_fdwrite(struct sock *f, void *data, int count)
34187bc9f6aSratchov {
34287bc9f6aSratchov 	int n;
34387bc9f6aSratchov 
34487bc9f6aSratchov 	n = write(f->fd, data, count);
345f933f6d7Sratchov 	if (n == -1) {
34687bc9f6aSratchov #ifdef DEBUG
34787bc9f6aSratchov 		if (errno == EFAULT) {
3487b639200Sratchov 			logx(0, "%s: fault", __func__);
34987bc9f6aSratchov 			panic();
35087bc9f6aSratchov 		}
35187bc9f6aSratchov #endif
35287bc9f6aSratchov 		if (errno != EAGAIN) {
3537b639200Sratchov 			logx(1, "sock %d: write failed, errno = %d", f->fd, errno);
35487bc9f6aSratchov 			sock_close(f);
35587bc9f6aSratchov 		} else {
35687bc9f6aSratchov #ifdef DEBUG
3577b639200Sratchov 			logx(4, "sock %d: write blocked", f->fd);
35887bc9f6aSratchov #endif
35987bc9f6aSratchov 		}
36087bc9f6aSratchov 		return 0;
36187bc9f6aSratchov 	}
36287bc9f6aSratchov 	if (n == 0) {
36387bc9f6aSratchov 		sock_close(f);
36487bc9f6aSratchov 		return 0;
36587bc9f6aSratchov 	}
36687bc9f6aSratchov 	return n;
36787bc9f6aSratchov }
36887bc9f6aSratchov 
36987bc9f6aSratchov /*
3707c71888cSratchov  * read from the socket fd and handle errors
37187bc9f6aSratchov  */
37287bc9f6aSratchov int
37387bc9f6aSratchov sock_fdread(struct sock *f, void *data, int count)
37487bc9f6aSratchov {
37587bc9f6aSratchov 	int n;
37687bc9f6aSratchov 
37787bc9f6aSratchov 	n = read(f->fd, data, count);
378f933f6d7Sratchov 	if (n == -1) {
37987bc9f6aSratchov #ifdef DEBUG
38087bc9f6aSratchov 		if (errno == EFAULT) {
3817b639200Sratchov 			logx(0, "%s: fault", __func__);
38287bc9f6aSratchov 			panic();
38387bc9f6aSratchov 		}
38487bc9f6aSratchov #endif
38587bc9f6aSratchov 		if (errno != EAGAIN) {
3867b639200Sratchov 			logx(1, "sock %d: read failed, errno = %d", f->fd, errno);
38787bc9f6aSratchov 			sock_close(f);
38887bc9f6aSratchov 		} else {
38987bc9f6aSratchov #ifdef DEBUG
3907b639200Sratchov 			logx(4, "sock %d: read blocked", f->fd);
39187bc9f6aSratchov #endif
39287bc9f6aSratchov 		}
39387bc9f6aSratchov 		return 0;
39487bc9f6aSratchov 	}
39587bc9f6aSratchov 	if (n == 0) {
39687bc9f6aSratchov 		sock_close(f);
39787bc9f6aSratchov 		return 0;
39887bc9f6aSratchov 	}
39987bc9f6aSratchov 	return n;
40087bc9f6aSratchov }
40187bc9f6aSratchov 
40287bc9f6aSratchov /*
40387bc9f6aSratchov  * read the next message into f->rmsg, return 1 on success
40487bc9f6aSratchov  */
40587bc9f6aSratchov int
40687bc9f6aSratchov sock_rmsg(struct sock *f)
40787bc9f6aSratchov {
40887bc9f6aSratchov 	int n;
40987bc9f6aSratchov 	char *data;
41087bc9f6aSratchov 
41187bc9f6aSratchov #ifdef DEBUG
41287bc9f6aSratchov 	if (f->rtodo == 0) {
4137b639200Sratchov 		logx(0, "%s: sock %d: nothing to read", __func__, f->fd);
41487bc9f6aSratchov 		panic();
41587bc9f6aSratchov 	}
41687bc9f6aSratchov #endif
41787bc9f6aSratchov 	data = (char *)&f->rmsg + sizeof(struct amsg) - f->rtodo;
41887bc9f6aSratchov 	n = sock_fdread(f, data, f->rtodo);
41987bc9f6aSratchov 	if (n == 0)
42087bc9f6aSratchov 		return 0;
42187bc9f6aSratchov 	if (n < f->rtodo) {
42287bc9f6aSratchov 		f->rtodo -= n;
42387bc9f6aSratchov 		return 0;
42487bc9f6aSratchov 	}
42587bc9f6aSratchov 	f->rtodo = 0;
42687bc9f6aSratchov #ifdef DEBUG
4277b639200Sratchov 	logx(4, "sock %d: read full message", f->fd);
42887bc9f6aSratchov #endif
42987bc9f6aSratchov 	return 1;
43087bc9f6aSratchov }
43187bc9f6aSratchov 
43287bc9f6aSratchov /*
43387bc9f6aSratchov  * write the message in f->rmsg, return 1 on success
43487bc9f6aSratchov  */
43587bc9f6aSratchov int
43687bc9f6aSratchov sock_wmsg(struct sock *f)
43787bc9f6aSratchov {
43887bc9f6aSratchov 	int n;
43987bc9f6aSratchov 	char *data;
44087bc9f6aSratchov 
44187bc9f6aSratchov #ifdef DEBUG
44287bc9f6aSratchov 	if (f->wtodo == 0) {
4437b639200Sratchov 		logx(0, "%s: sock %d: already written", __func__, f->fd);
4447b639200Sratchov 		/* XXX: this is fatal and we should exit here */
44587bc9f6aSratchov 	}
44687bc9f6aSratchov #endif
44787bc9f6aSratchov 	data = (char *)&f->wmsg + sizeof(struct amsg) - f->wtodo;
44887bc9f6aSratchov 	n = sock_fdwrite(f, data, f->wtodo);
44987bc9f6aSratchov 	if (n == 0)
45087bc9f6aSratchov 		return 0;
45187bc9f6aSratchov 	if (n < f->wtodo) {
45287bc9f6aSratchov 		f->wtodo -= n;
45387bc9f6aSratchov 		return 0;
45487bc9f6aSratchov 	}
45587bc9f6aSratchov 	f->wtodo = 0;
45687bc9f6aSratchov #ifdef DEBUG
4577b639200Sratchov 	logx(4, "sock %d: wrote full message", f->fd);
45887bc9f6aSratchov #endif
45987bc9f6aSratchov 	return 1;
46087bc9f6aSratchov }
46187bc9f6aSratchov 
46287bc9f6aSratchov /*
46387bc9f6aSratchov  * read data into the slot/midi ring buffer
46487bc9f6aSratchov  */
46587bc9f6aSratchov int
46687bc9f6aSratchov sock_rdata(struct sock *f)
46787bc9f6aSratchov {
46807826207Sratchov 	unsigned char midibuf[MIDI_BUFSZ];
46987bc9f6aSratchov 	unsigned char *data;
47087bc9f6aSratchov 	int n, count;
47187bc9f6aSratchov 
47287bc9f6aSratchov #ifdef DEBUG
47387bc9f6aSratchov 	if (f->rtodo == 0) {
4747b639200Sratchov 		logx(0, "%s: sock %d: data block already read", __func__, f->fd);
47587bc9f6aSratchov 		panic();
47687bc9f6aSratchov 	}
47787bc9f6aSratchov #endif
47887bc9f6aSratchov 	while (f->rtodo > 0) {
47907826207Sratchov 		if (f->slot)
48007826207Sratchov 			data = abuf_wgetblk(&f->slot->mix.buf, &count);
48107826207Sratchov 		else {
48207826207Sratchov 			data = midibuf;
48307826207Sratchov 			count = MIDI_BUFSZ;
48407826207Sratchov 		}
48587bc9f6aSratchov 		if (count > f->rtodo)
48687bc9f6aSratchov 			count = f->rtodo;
48787bc9f6aSratchov 		n = sock_fdread(f, data, count);
48887bc9f6aSratchov 		if (n == 0)
48987bc9f6aSratchov 			return 0;
49087bc9f6aSratchov 		f->rtodo -= n;
49107826207Sratchov 		if (f->slot)
49207826207Sratchov 			abuf_wcommit(&f->slot->mix.buf, n);
49307826207Sratchov 		else
49407826207Sratchov 			midi_in(f->midi, midibuf, n);
49587bc9f6aSratchov 	}
49687bc9f6aSratchov #ifdef DEBUG
4977b639200Sratchov 	logx(4, "sock %d: read complete block", f->fd);
49887bc9f6aSratchov #endif
49987bc9f6aSratchov 	if (f->slot)
50087bc9f6aSratchov 		slot_write(f->slot);
50187bc9f6aSratchov 	return 1;
50287bc9f6aSratchov }
50387bc9f6aSratchov 
50487bc9f6aSratchov /*
5057c71888cSratchov  * write data to the slot/midi ring buffer
50687bc9f6aSratchov  */
50787bc9f6aSratchov int
50887bc9f6aSratchov sock_wdata(struct sock *f)
50987bc9f6aSratchov {
51087bc9f6aSratchov 	static unsigned char dummy[AMSG_DATAMAX];
51187bc9f6aSratchov 	unsigned char *data = NULL;
51287bc9f6aSratchov 	int n, count;
51387bc9f6aSratchov 
51487bc9f6aSratchov #ifdef DEBUG
51587bc9f6aSratchov 	if (f->wtodo == 0) {
5167b639200Sratchov 		logx(0, "%s: sock %d: zero-sized data block", __func__, f->fd);
51787bc9f6aSratchov 		panic();
51887bc9f6aSratchov 	}
51987bc9f6aSratchov #endif
52087bc9f6aSratchov 	if (f->pstate == SOCK_STOP) {
52187bc9f6aSratchov 		while (f->wtodo > 0) {
52287bc9f6aSratchov 			n = sock_fdwrite(f, dummy, f->wtodo);
52387bc9f6aSratchov 			if (n == 0)
52487bc9f6aSratchov 				return 0;
52587bc9f6aSratchov 			f->wtodo -= n;
52687bc9f6aSratchov 		}
52787bc9f6aSratchov #ifdef DEBUG
5287b639200Sratchov 		logx(4, "sock %d: zero-filled remaining block", f->fd);
52987bc9f6aSratchov #endif
53087bc9f6aSratchov 		return 1;
53187bc9f6aSratchov 	}
53287bc9f6aSratchov 	while (f->wtodo > 0) {
533f2c5b8a8Sratchov 		/*
534f2c5b8a8Sratchov 		 * f->slot and f->midi are set by sock_hello(), so
535f2c5b8a8Sratchov 		 * count is always properly initialized
536f2c5b8a8Sratchov 		 */
5377947a9ddSratchov 		if (f->slot)
5387947a9ddSratchov 			data = abuf_rgetblk(&f->slot->sub.buf, &count);
5397947a9ddSratchov 		else if (f->midi)
5407947a9ddSratchov 			data = abuf_rgetblk(&f->midi->obuf, &count);
541d07fece6Sratchov 		else {
5423e5ee6d4Sratchov 			data = f->ctldesc + (f->wsize - f->wtodo);
543d07fece6Sratchov 			count = f->wtodo;
544d07fece6Sratchov 		}
54587bc9f6aSratchov 		if (count > f->wtodo)
54687bc9f6aSratchov 			count = f->wtodo;
54787bc9f6aSratchov 		n = sock_fdwrite(f, data, count);
54887bc9f6aSratchov 		if (n == 0)
54987bc9f6aSratchov 			return 0;
55087bc9f6aSratchov 		f->wtodo -= n;
5517947a9ddSratchov 		if (f->slot)
5527947a9ddSratchov 			abuf_rdiscard(&f->slot->sub.buf, n);
5537947a9ddSratchov 		else if (f->midi)
5547947a9ddSratchov 			abuf_rdiscard(&f->midi->obuf, n);
55587bc9f6aSratchov 	}
55687bc9f6aSratchov 	if (f->slot)
55787bc9f6aSratchov 		slot_read(f->slot);
55887bc9f6aSratchov 	if (f->midi)
55987bc9f6aSratchov 		midi_fill(f->midi);
56087bc9f6aSratchov #ifdef DEBUG
5617b639200Sratchov 	logx(4, "sock %d: wrote complete block", f->fd);
56287bc9f6aSratchov #endif
56387bc9f6aSratchov 	return 1;
56487bc9f6aSratchov }
56587bc9f6aSratchov 
56687bc9f6aSratchov int
56787bc9f6aSratchov sock_setpar(struct sock *f)
56887bc9f6aSratchov {
56987bc9f6aSratchov 	struct slot *s = f->slot;
570c67d5b9aSratchov 	struct dev *d = s->opt->dev;
57187bc9f6aSratchov 	struct amsg_par *p = &f->rmsg.u.par;
572bc4f1c78Sratchov 	unsigned int min, max;
573bc4f1c78Sratchov 	uint32_t rate, appbufsz;
574bc4f1c78Sratchov 	uint16_t pchan, rchan;
57587bc9f6aSratchov 
57687bc9f6aSratchov 	rchan = ntohs(p->rchan);
57787bc9f6aSratchov 	pchan = ntohs(p->pchan);
57887bc9f6aSratchov 	appbufsz = ntohl(p->appbufsz);
57987bc9f6aSratchov 	rate = ntohl(p->rate);
58087bc9f6aSratchov 
58187bc9f6aSratchov 	if (AMSG_ISSET(p->bits)) {
58287bc9f6aSratchov 		if (p->bits < BITS_MIN || p->bits > BITS_MAX) {
58387bc9f6aSratchov #ifdef DEBUG
5847b639200Sratchov 			logx(1, "sock %d: %d: bits out of bounds", f->fd, p->bits);
58587bc9f6aSratchov #endif
58687bc9f6aSratchov 			return 0;
58787bc9f6aSratchov 		}
58887bc9f6aSratchov 		if (AMSG_ISSET(p->bps)) {
58987bc9f6aSratchov 			if (p->bps < ((p->bits + 7) / 8) || p->bps > 4) {
59087bc9f6aSratchov #ifdef DEBUG
5917b639200Sratchov 				logx(1, "sock %d: %d: wrong bytes per sample",
5927b639200Sratchov 				    f->fd, p->bps);
59387bc9f6aSratchov #endif
59487bc9f6aSratchov 				return 0;
59587bc9f6aSratchov 			}
59687bc9f6aSratchov 		} else
59787bc9f6aSratchov 			p->bps = APARAMS_BPS(p->bits);
59887bc9f6aSratchov 		s->par.bits = p->bits;
59987bc9f6aSratchov 		s->par.bps = p->bps;
60087bc9f6aSratchov 	}
60187bc9f6aSratchov 	if (AMSG_ISSET(p->sig))
60287bc9f6aSratchov 		s->par.sig = p->sig ? 1 : 0;
60387bc9f6aSratchov 	if (AMSG_ISSET(p->le))
60487bc9f6aSratchov 		s->par.le = p->le ? 1 : 0;
60587bc9f6aSratchov 	if (AMSG_ISSET(p->msb))
60687bc9f6aSratchov 		s->par.msb = p->msb ? 1 : 0;
60787bc9f6aSratchov 	if (AMSG_ISSET(rchan) && (s->mode & MODE_RECMASK)) {
60887bc9f6aSratchov 		if (rchan < 1)
60987bc9f6aSratchov 			rchan = 1;
610875135e3Sratchov 		else if (rchan > NCHAN_MAX)
61187bc9f6aSratchov 			rchan = NCHAN_MAX;
61223321a5cSratchov 		s->sub.nch = rchan;
61387bc9f6aSratchov 	}
61487bc9f6aSratchov 	if (AMSG_ISSET(pchan) && (s->mode & MODE_PLAY)) {
61587bc9f6aSratchov 		if (pchan < 1)
61687bc9f6aSratchov 			pchan = 1;
617875135e3Sratchov 		else if (pchan > NCHAN_MAX)
61887bc9f6aSratchov 			pchan = NCHAN_MAX;
61923321a5cSratchov 		s->mix.nch = pchan;
62087bc9f6aSratchov 	}
62187bc9f6aSratchov 	if (AMSG_ISSET(rate)) {
62287bc9f6aSratchov 		if (rate < RATE_MIN)
62387bc9f6aSratchov 			rate = RATE_MIN;
624875135e3Sratchov 		else if (rate > RATE_MAX)
62587bc9f6aSratchov 			rate = RATE_MAX;
62687bc9f6aSratchov 		s->round = dev_roundof(d, rate);
62787bc9f6aSratchov 		s->rate = rate;
6287b639200Sratchov 		if (!AMSG_ISSET(appbufsz))
62987bc9f6aSratchov 			appbufsz = d->bufsz / d->round * s->round;
63087bc9f6aSratchov 	}
63187bc9f6aSratchov 	if (AMSG_ISSET(p->xrun)) {
63287bc9f6aSratchov 		if (p->xrun != XRUN_IGNORE &&
63387bc9f6aSratchov 		    p->xrun != XRUN_SYNC &&
63487bc9f6aSratchov 		    p->xrun != XRUN_ERROR) {
63587bc9f6aSratchov #ifdef DEBUG
6367b639200Sratchov 			logx(1, "sock %d: %u: bad xrun policy", f->fd, p->xrun);
63787bc9f6aSratchov #endif
63887bc9f6aSratchov 			return 0;
63987bc9f6aSratchov 		}
64087bc9f6aSratchov 		s->xrun = p->xrun;
6410e6be583Sratchov 		if (s->opt->mtc != NULL && s->xrun == XRUN_IGNORE)
64287bc9f6aSratchov 			s->xrun = XRUN_SYNC;
64387bc9f6aSratchov 	}
64487bc9f6aSratchov 	if (AMSG_ISSET(appbufsz)) {
64587bc9f6aSratchov 		rate = s->rate;
64687bc9f6aSratchov 		min = 1;
64787bc9f6aSratchov 		max = 1 + rate / d->round;
64887bc9f6aSratchov 		min *= s->round;
64987bc9f6aSratchov 		max *= s->round;
650fd3cf84aSratchov 		appbufsz += s->round / 2;
65187bc9f6aSratchov 		appbufsz -= appbufsz % s->round;
65287bc9f6aSratchov 		if (appbufsz < min)
65387bc9f6aSratchov 			appbufsz = min;
65487bc9f6aSratchov 		if (appbufsz > max)
65587bc9f6aSratchov 			appbufsz = max;
65687bc9f6aSratchov 		s->appbufsz = appbufsz;
65787bc9f6aSratchov 	}
65887bc9f6aSratchov 	return 1;
65987bc9f6aSratchov }
66087bc9f6aSratchov 
66187bc9f6aSratchov int
66287bc9f6aSratchov sock_auth(struct sock *f)
66387bc9f6aSratchov {
66487bc9f6aSratchov 	struct amsg_auth *p = &f->rmsg.u.auth;
66572ca3e97Sratchov 	uid_t euid;
66672ca3e97Sratchov 	gid_t egid;
66772ca3e97Sratchov 
66872ca3e97Sratchov 	/*
669d9a51c35Sjmc 	 * root bypasses any authentication checks and has no session
67072ca3e97Sratchov 	 */
67172ca3e97Sratchov 	if (getpeereid(f->fd, &euid, &egid) == 0 && euid == 0) {
67272ca3e97Sratchov 		f->pstate = SOCK_HELLO;
67372ca3e97Sratchov 		f->sesrefs = 0;
67472ca3e97Sratchov 		return 1;
67572ca3e97Sratchov 	}
67687bc9f6aSratchov 
67787bc9f6aSratchov 	if (sock_sesrefs == 0) {
67887bc9f6aSratchov 		/* start a new session */
67987bc9f6aSratchov 		memcpy(sock_sescookie, p->cookie, AMSG_COOKIELEN);
68072ca3e97Sratchov 		f->sesrefs = 1;
68187bc9f6aSratchov 	} else if (memcmp(sock_sescookie, p->cookie, AMSG_COOKIELEN) != 0) {
68287bc9f6aSratchov 		/* another session is active, drop connection */
68387bc9f6aSratchov 		return 0;
68487bc9f6aSratchov 	}
68572ca3e97Sratchov 	sock_sesrefs += f->sesrefs;
68687bc9f6aSratchov 	f->pstate = SOCK_HELLO;
68787bc9f6aSratchov 	return 1;
68887bc9f6aSratchov }
68987bc9f6aSratchov 
69087bc9f6aSratchov int
69187bc9f6aSratchov sock_hello(struct sock *f)
69287bc9f6aSratchov {
69387bc9f6aSratchov 	struct amsg_hello *p = &f->rmsg.u.hello;
69487bc9f6aSratchov 	struct port *c;
695f218ca2bSratchov 	struct opt *opt;
69687bc9f6aSratchov 	unsigned int mode;
6972988007fSratchov 	unsigned int id;
69887bc9f6aSratchov 
69987bc9f6aSratchov 	mode = ntohs(p->mode);
7002988007fSratchov 	id = ntohl(p->id);
70187bc9f6aSratchov #ifdef DEBUG
7027b639200Sratchov 	logx(3, "sock %d: hello from <%s>, mode %x, ver %d",
7037b639200Sratchov 	    f->fd, p->who, mode, p->version);
70487bc9f6aSratchov #endif
70587bc9f6aSratchov 	if (p->version != AMSG_VERSION) {
7067b639200Sratchov 		logx(1, "sock %d: %u: unsupported version", f->fd, p->version);
70787bc9f6aSratchov 		return 0;
70887bc9f6aSratchov 	}
70987bc9f6aSratchov 	switch (mode) {
71087bc9f6aSratchov 	case MODE_MIDIIN:
71187bc9f6aSratchov 	case MODE_MIDIOUT:
71287bc9f6aSratchov 	case MODE_MIDIOUT | MODE_MIDIIN:
71387bc9f6aSratchov 	case MODE_REC:
71487bc9f6aSratchov 	case MODE_PLAY:
71587bc9f6aSratchov 	case MODE_PLAY | MODE_REC:
716d07fece6Sratchov 	case MODE_CTLREAD:
717d07fece6Sratchov 	case MODE_CTLWRITE:
718d07fece6Sratchov 	case MODE_CTLREAD | MODE_CTLWRITE:
71987bc9f6aSratchov 		break;
72087bc9f6aSratchov 	default:
72187bc9f6aSratchov #ifdef DEBUG
7227b639200Sratchov 		logx(1, "sock %d: %u: unsupported mode", f->fd, mode);
72387bc9f6aSratchov #endif
72487bc9f6aSratchov 		return 0;
72587bc9f6aSratchov 	}
72687bc9f6aSratchov 	f->pstate = SOCK_INIT;
7279fd7fddfSratchov 	f->port = NULL;
7286bd694d1Sratchov 	if (mode & MODE_MIDIMASK) {
72987bc9f6aSratchov 		f->slot = NULL;
73087bc9f6aSratchov 		f->midi = midi_new(&sock_midiops, f, mode);
73187bc9f6aSratchov 		if (f->midi == NULL)
73287bc9f6aSratchov 			return 0;
73387bc9f6aSratchov 		/* XXX: add 'devtype' to libsndio */
73436355b88Sratchov 		if (p->devnum == AMSG_NODEV) {
73536355b88Sratchov 			opt = opt_byname(p->opt);
7365fb8f3c9Sratchov 			if (opt == NULL)
7375fb8f3c9Sratchov 				return 0;
73836355b88Sratchov 			if (!opt_ref(opt))
73936355b88Sratchov 				return 0;
74036355b88Sratchov 			midi_tag(f->midi, opt->num);
74136355b88Sratchov 		} else if (p->devnum < 16) {
74236355b88Sratchov 			opt = legacy_opt(p->devnum, p->opt);
74336355b88Sratchov 			if (opt == NULL)
74436355b88Sratchov 				return 0;
74536355b88Sratchov 			if (!opt_ref(opt))
746326545d4Sratchov 				return 0;
7475fb8f3c9Sratchov 			midi_tag(f->midi, opt->num);
74887bc9f6aSratchov 		} else if (p->devnum < 32) {
74987bc9f6aSratchov 			midi_tag(f->midi, p->devnum);
75087bc9f6aSratchov 		} else if (p->devnum < 48) {
75136355b88Sratchov 			c = port_alt_ref(p->devnum - 32);
75236355b88Sratchov 			if (c == NULL)
75387bc9f6aSratchov 				return 0;
7549fd7fddfSratchov 			f->port = c;
75507826207Sratchov 			midi_link(f->midi, c->midi);
75687bc9f6aSratchov 		} else
75787bc9f6aSratchov 			return 0;
75887bc9f6aSratchov 		return 1;
75987bc9f6aSratchov 	}
760d07fece6Sratchov 	if (mode & MODE_CTLMASK) {
76136355b88Sratchov 		if (p->devnum == AMSG_NODEV) {
76236355b88Sratchov 			opt = opt_byname(p->opt);
763bb6cfcd4Sratchov 			if (opt == NULL)
764bb6cfcd4Sratchov 				return 0;
76536355b88Sratchov 		} else {
76636355b88Sratchov 			opt = legacy_opt(p->devnum, p->opt);
76736355b88Sratchov 			if (opt == NULL)
76836355b88Sratchov 				return 0;
76936355b88Sratchov 		}
770bb6cfcd4Sratchov 		f->ctlslot = ctlslot_new(opt, &sock_ctlops, f);
771d07fece6Sratchov 		if (f->ctlslot == NULL) {
7727b639200Sratchov 			logx(2, "sock %d: couldn't get ctlslot", f->fd);
773d07fece6Sratchov 			return 0;
774d07fece6Sratchov 		}
7753e5ee6d4Sratchov 		f->ctldesc = xmalloc(SOCK_CTLDESC_SIZE);
776d07fece6Sratchov 		f->ctlops = 0;
777d07fece6Sratchov 		f->ctlsyncpending = 0;
778d07fece6Sratchov 		return 1;
779d07fece6Sratchov 	}
78036355b88Sratchov 	opt = (p->devnum == AMSG_NODEV) ?
78136355b88Sratchov 	    opt_byname(p->opt) : legacy_opt(p->devnum, p->opt);
782f218ca2bSratchov 	if (opt == NULL)
78387bc9f6aSratchov 		return 0;
784c67d5b9aSratchov 	f->slot = slot_new(opt, id, p->who, &sock_slotops, f, mode);
785edbb4404Sratchov 	if (f->slot == NULL)
78687bc9f6aSratchov 		return 0;
78787bc9f6aSratchov 	f->midi = NULL;
78887bc9f6aSratchov 	return 1;
78987bc9f6aSratchov }
79087bc9f6aSratchov 
79187bc9f6aSratchov /*
79287bc9f6aSratchov  * execute the message in f->rmsg, return 1 on success
79387bc9f6aSratchov  */
79487bc9f6aSratchov int
79587bc9f6aSratchov sock_execmsg(struct sock *f)
79687bc9f6aSratchov {
797d07fece6Sratchov 	struct ctl *c;
79887bc9f6aSratchov 	struct slot *s = f->slot;
79987bc9f6aSratchov 	struct amsg *m = &f->rmsg;
800*0cdbd964Sratchov 	struct conv conv;
80187bc9f6aSratchov 	unsigned char *data;
80255a46d88Sratchov 	unsigned int size, ctl;
8033e5ee6d4Sratchov 	int cmd;
80487bc9f6aSratchov 
8053e5ee6d4Sratchov 	cmd = ntohl(m->cmd);
8063e5ee6d4Sratchov 	switch (cmd) {
80787bc9f6aSratchov 	case AMSG_DATA:
80887bc9f6aSratchov #ifdef DEBUG
8097b639200Sratchov 		logx(4, "sock %d: DATA message", f->fd);
81087bc9f6aSratchov #endif
81187bc9f6aSratchov 		if (s != NULL && f->pstate != SOCK_START) {
81287bc9f6aSratchov #ifdef DEBUG
8137b639200Sratchov 			logx(1, "sock %d: DATA, wrong state", f->fd);
81487bc9f6aSratchov #endif
81587bc9f6aSratchov 			sock_close(f);
81687bc9f6aSratchov 			return 0;
81787bc9f6aSratchov 		}
81887bc9f6aSratchov 		if ((f->slot && !(f->slot->mode & MODE_PLAY)) ||
81987bc9f6aSratchov 		    (f->midi && !(f->midi->mode & MODE_MIDIOUT))) {
82087bc9f6aSratchov #ifdef DEBUG
8217b639200Sratchov 			logx(1, "sock %d: DATA, input-only mode", f->fd);
82287bc9f6aSratchov #endif
82387bc9f6aSratchov 			sock_close(f);
82487bc9f6aSratchov 			return 0;
82587bc9f6aSratchov 		}
82687bc9f6aSratchov 		size = ntohl(m->u.data.size);
82755a46d88Sratchov 		if (size == 0) {
82887bc9f6aSratchov #ifdef DEBUG
8297b639200Sratchov 			logx(1, "sock %d: zero size payload", f->fd);
83087bc9f6aSratchov #endif
83187bc9f6aSratchov 			sock_close(f);
83287bc9f6aSratchov 			return 0;
83387bc9f6aSratchov 		}
83487bc9f6aSratchov 		if (s != NULL && size % s->mix.bpf != 0) {
83587bc9f6aSratchov #ifdef DEBUG
8367b639200Sratchov 			logx(1, "sock %d: not aligned to frame", f->fd);
83787bc9f6aSratchov #endif
83887bc9f6aSratchov 			sock_close(f);
83987bc9f6aSratchov 			return 0;
84087bc9f6aSratchov 		}
84187bc9f6aSratchov 		if (s != NULL && size > f->ralign) {
84287bc9f6aSratchov #ifdef DEBUG
8437b639200Sratchov 			logx(1, "sock %d: size = %d, ralign = %d: "
8447b639200Sratchov 			   "not aligned to block", f->fd, size, f->ralign);
84587bc9f6aSratchov #endif
84687bc9f6aSratchov 			sock_close(f);
84787bc9f6aSratchov 			return 0;
84887bc9f6aSratchov 		}
84987bc9f6aSratchov 		f->rstate = SOCK_RDATA;
85087bc9f6aSratchov 		f->rsize = f->rtodo = size;
85187bc9f6aSratchov 		if (s != NULL) {
85287bc9f6aSratchov 			f->ralign -= size;
85387bc9f6aSratchov 			if (f->ralign == 0)
85487bc9f6aSratchov 				f->ralign = s->round * s->mix.bpf;
85587bc9f6aSratchov 		}
85687bc9f6aSratchov 		if (f->rtodo > f->rmax) {
85787bc9f6aSratchov #ifdef DEBUG
8587b639200Sratchov 			logx(1, "sock %d: unexpected data, size = %u, rmax = %d",
8597b639200Sratchov 			    f->fd, size, f->rmax);
86087bc9f6aSratchov #endif
86187bc9f6aSratchov 			sock_close(f);
86287bc9f6aSratchov 			return 0;
86387bc9f6aSratchov 		}
86487bc9f6aSratchov 		f->rmax -= f->rtodo;
86587bc9f6aSratchov 		if (f->rtodo == 0) {
86687bc9f6aSratchov #ifdef DEBUG
8677b639200Sratchov 			logx(1, "sock %d: zero-length data chunk", f->fd);
86887bc9f6aSratchov #endif
86987bc9f6aSratchov 			sock_close(f);
87087bc9f6aSratchov 			return 0;
87187bc9f6aSratchov 		}
87287bc9f6aSratchov 		break;
87387bc9f6aSratchov 	case AMSG_START:
87487bc9f6aSratchov #ifdef DEBUG
8757b639200Sratchov 		logx(3, "sock %d: START message", f->fd);
87687bc9f6aSratchov #endif
8777947a9ddSratchov 		if (f->pstate != SOCK_INIT || s == NULL) {
87887bc9f6aSratchov #ifdef DEBUG
8797b639200Sratchov 			logx(1, "sock %d: START, wrong state", f->fd);
88087bc9f6aSratchov #endif
88187bc9f6aSratchov 			sock_close(f);
88287bc9f6aSratchov 			return 0;
88387bc9f6aSratchov 		}
88487bc9f6aSratchov 		f->tickpending = 0;
88587bc9f6aSratchov 		f->stoppending = 0;
88687bc9f6aSratchov 		slot_start(s);
88787bc9f6aSratchov 		if (s->mode & MODE_PLAY) {
88887bc9f6aSratchov 			f->fillpending = s->appbufsz;
88987bc9f6aSratchov 			f->ralign = s->round * s->mix.bpf;
89087bc9f6aSratchov 			f->rmax = 0;
89187bc9f6aSratchov 		}
89287bc9f6aSratchov 		if (s->mode & MODE_RECMASK) {
89387bc9f6aSratchov 			f->walign = s->round * s->sub.bpf;
89487bc9f6aSratchov 			f->wmax = 0;
89587bc9f6aSratchov 		}
89687bc9f6aSratchov 		f->pstate = SOCK_START;
89787bc9f6aSratchov 		f->rstate = SOCK_RMSG;
89887bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
89987bc9f6aSratchov 		break;
90087bc9f6aSratchov 	case AMSG_STOP:
90187bc9f6aSratchov #ifdef DEBUG
9027b639200Sratchov 		logx(3, "sock %d: STOP message", f->fd);
90387bc9f6aSratchov #endif
90487bc9f6aSratchov 		if (f->pstate != SOCK_START) {
90587bc9f6aSratchov #ifdef DEBUG
9067b639200Sratchov 			logx(1, "sock %d: STOP, wrong state", f->fd);
90787bc9f6aSratchov #endif
90887bc9f6aSratchov 			sock_close(f);
90987bc9f6aSratchov 			return 0;
91087bc9f6aSratchov 		}
91187bc9f6aSratchov 		f->rmax = 0;
91287bc9f6aSratchov 		if (!(s->mode & MODE_PLAY))
91387bc9f6aSratchov 			f->stoppending = 1;
91487bc9f6aSratchov 		f->pstate = SOCK_STOP;
91587bc9f6aSratchov 		f->rstate = SOCK_RMSG;
91687bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
91787bc9f6aSratchov 		if (s->mode & MODE_PLAY) {
91887bc9f6aSratchov 			if (f->ralign < s->round * s->mix.bpf) {
91987bc9f6aSratchov 				data = abuf_wgetblk(&s->mix.buf, &size);
92087bc9f6aSratchov #ifdef DEBUG
92187bc9f6aSratchov 				if (size < f->ralign) {
9227b639200Sratchov 					logx(0, "sock %d: unaligned stop, "
9237b639200Sratchov 					    "size = %u, ralign = %u",
9247b639200Sratchov 					    f->fd, size, f->ralign);
92587bc9f6aSratchov 					panic();
92687bc9f6aSratchov 				}
92787bc9f6aSratchov #endif
928*0cdbd964Sratchov 				enc_init(&conv, &s->par, s->mix.nch);
929*0cdbd964Sratchov 				enc_sil_do(&conv, data, f->ralign / s->mix.bpf);
93087bc9f6aSratchov 				abuf_wcommit(&s->mix.buf, f->ralign);
93187bc9f6aSratchov 				f->ralign = s->round * s->mix.bpf;
93287bc9f6aSratchov 			}
93387bc9f6aSratchov 		}
934ec8a3410Sratchov 		slot_stop(s, AMSG_ISSET(m->u.stop.drain) ? m->u.stop.drain : 1);
93587bc9f6aSratchov 		break;
93687bc9f6aSratchov 	case AMSG_SETPAR:
93787bc9f6aSratchov #ifdef DEBUG
9387b639200Sratchov 		logx(3, "sock %d: SETPAR message", f->fd);
93987bc9f6aSratchov #endif
9407947a9ddSratchov 		if (f->pstate != SOCK_INIT || s == NULL) {
94187bc9f6aSratchov #ifdef DEBUG
9427b639200Sratchov 			logx(1, "sock %d: SETPAR, wrong state", f->fd);
94387bc9f6aSratchov #endif
94487bc9f6aSratchov 			sock_close(f);
94587bc9f6aSratchov 			return 0;
94687bc9f6aSratchov 		}
94787bc9f6aSratchov 		if (!sock_setpar(f)) {
94887bc9f6aSratchov 			sock_close(f);
94987bc9f6aSratchov 			return 0;
95087bc9f6aSratchov 		}
95187bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
95287bc9f6aSratchov 		f->rstate = SOCK_RMSG;
95387bc9f6aSratchov 		break;
95487bc9f6aSratchov 	case AMSG_GETPAR:
95587bc9f6aSratchov #ifdef DEBUG
9567b639200Sratchov 		logx(3, "sock %d: GETPAR message", f->fd);
95787bc9f6aSratchov #endif
9587947a9ddSratchov 		if (f->pstate != SOCK_INIT || s == NULL) {
95987bc9f6aSratchov #ifdef DEBUG
9607b639200Sratchov 			logx(1, "sock %d: GETPAR, wrong state", f->fd);
96187bc9f6aSratchov #endif
96287bc9f6aSratchov 			sock_close(f);
96387bc9f6aSratchov 			return 0;
96487bc9f6aSratchov 		}
96587bc9f6aSratchov 		AMSG_INIT(m);
96687bc9f6aSratchov 		m->cmd = htonl(AMSG_GETPAR);
96787bc9f6aSratchov 		m->u.par.legacy_mode = s->mode;
968c095e34bSratchov 		m->u.par.xrun = s->xrun;
96987bc9f6aSratchov 		m->u.par.bits = s->par.bits;
97087bc9f6aSratchov 		m->u.par.bps = s->par.bps;
97187bc9f6aSratchov 		m->u.par.sig = s->par.sig;
97287bc9f6aSratchov 		m->u.par.le = s->par.le;
97387bc9f6aSratchov 		m->u.par.msb = s->par.msb;
97423321a5cSratchov 		if (s->mode & MODE_PLAY)
97523321a5cSratchov 			m->u.par.pchan = htons(s->mix.nch);
97623321a5cSratchov 		if (s->mode & MODE_RECMASK)
97723321a5cSratchov 			m->u.par.rchan = htons(s->sub.nch);
97887bc9f6aSratchov 		m->u.par.rate = htonl(s->rate);
97987bc9f6aSratchov 		m->u.par.appbufsz = htonl(s->appbufsz);
98087bc9f6aSratchov 		m->u.par.bufsz = htonl(SLOT_BUFSZ(s));
98187bc9f6aSratchov 		m->u.par.round = htonl(s->round);
98287bc9f6aSratchov 		f->rstate = SOCK_RRET;
98387bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
98487bc9f6aSratchov 		break;
98587bc9f6aSratchov 	case AMSG_SETVOL:
98687bc9f6aSratchov #ifdef DEBUG
9877b639200Sratchov 		logx(3, "sock %d: SETVOL message", f->fd);
98887bc9f6aSratchov #endif
9897947a9ddSratchov 		if (f->pstate < SOCK_INIT || s == NULL) {
99087bc9f6aSratchov #ifdef DEBUG
9917b639200Sratchov 			logx(1, "sock %d: SETVOL, wrong state", f->fd);
99287bc9f6aSratchov #endif
99387bc9f6aSratchov 			sock_close(f);
99487bc9f6aSratchov 			return 0;
99587bc9f6aSratchov 		}
99687bc9f6aSratchov 		ctl = ntohl(m->u.vol.ctl);
99787bc9f6aSratchov 		if (ctl > MIDI_MAXCTL) {
99887bc9f6aSratchov #ifdef DEBUG
9997b639200Sratchov 			logx(1, "sock %d: SETVOL, volume out of range", f->fd);
100087bc9f6aSratchov #endif
100187bc9f6aSratchov 			sock_close(f);
100287bc9f6aSratchov 			return 0;
100387bc9f6aSratchov 		}
100487bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
100587bc9f6aSratchov 		f->rstate = SOCK_RMSG;
100687bc9f6aSratchov 		f->lastvol = ctl; /* dont trigger feedback message */
100787bc9f6aSratchov 		slot_setvol(s, ctl);
1008c67d5b9aSratchov 		dev_midi_vol(s->opt->dev, s);
100999580020Sratchov 		ctl_onval(CTL_SLOT_LEVEL, s, NULL, ctl);
1010d07fece6Sratchov 		break;
10114d8b188fSratchov 	case AMSG_CTLSUB_OLD:
1012d07fece6Sratchov 	case AMSG_CTLSUB:
1013d07fece6Sratchov #ifdef DEBUG
10147b639200Sratchov 		logx(3, "sock %d: CTLSUB message, desc = 0x%x, val = 0x%x",
10157b639200Sratchov 		    f->fd, m->u.ctlsub.desc, m->u.ctlsub.val);
1016d07fece6Sratchov #endif
1017d07fece6Sratchov 		if (f->pstate != SOCK_INIT || f->ctlslot == NULL) {
1018d07fece6Sratchov #ifdef DEBUG
10197b639200Sratchov 			logx(1, "sock %d: CTLSUB, wrong state", f->fd);
1020d07fece6Sratchov #endif
1021d07fece6Sratchov 			sock_close(f);
1022d07fece6Sratchov 			return 0;
1023d07fece6Sratchov 		}
1024d07fece6Sratchov 		if (m->u.ctlsub.desc) {
1025d07fece6Sratchov 			if (!(f->ctlops & SOCK_CTLDESC)) {
1026c7054416Sratchov 				ctl = f->ctlslot->self;
102799580020Sratchov 				c = ctl_list;
1028d07fece6Sratchov 				while (c != NULL) {
102999580020Sratchov 					if (ctlslot_visible(f->ctlslot, c))
1030d07fece6Sratchov 						c->desc_mask |= ctl;
1031d07fece6Sratchov 					c = c->next;
1032d07fece6Sratchov 				}
1033d07fece6Sratchov 				f->ctlops |= SOCK_CTLDESC;
1034d07fece6Sratchov 				f->ctlsyncpending = 1;
10354d8b188fSratchov 				f->ctl_desc_size = (cmd == AMSG_CTLSUB) ?
10364d8b188fSratchov 				    sizeof(struct amsg_ctl_desc) :
10374d8b188fSratchov 				    AMSG_OLD_DESC_SIZE;
1038e575fbdeSratchov 			}
1039d07fece6Sratchov 		} else
1040d07fece6Sratchov 			f->ctlops &= ~SOCK_CTLDESC;
1041d07fece6Sratchov 		if (m->u.ctlsub.val) {
1042d07fece6Sratchov 			f->ctlops |= SOCK_CTLVAL;
1043d07fece6Sratchov 		} else
1044d07fece6Sratchov 			f->ctlops &= ~SOCK_CTLVAL;
1045d07fece6Sratchov 		f->rstate = SOCK_RMSG;
1046d07fece6Sratchov 		f->rtodo = sizeof(struct amsg);
1047d07fece6Sratchov 		break;
1048d07fece6Sratchov 	case AMSG_CTLSET:
1049d07fece6Sratchov #ifdef DEBUG
10507b639200Sratchov 		logx(3, "sock %d: CTLSET message", f->fd);
1051d07fece6Sratchov #endif
1052d07fece6Sratchov 		if (f->pstate < SOCK_INIT || f->ctlslot == NULL) {
1053d07fece6Sratchov #ifdef DEBUG
10547b639200Sratchov 			logx(1, "sock %d: CTLSET, wrong state", f->fd);
1055d07fece6Sratchov #endif
1056d07fece6Sratchov 			sock_close(f);
1057d07fece6Sratchov 			return 0;
1058d07fece6Sratchov 		}
105999580020Sratchov 
106099580020Sratchov 		c = ctlslot_lookup(f->ctlslot, ntohs(m->u.ctlset.addr));
106199580020Sratchov 		if (c == NULL) {
1062d07fece6Sratchov #ifdef DEBUG
10637b639200Sratchov 			logx(1, "sock %d: CTLSET, wrong addr", f->fd);
106499580020Sratchov #endif
106599580020Sratchov 			sock_close(f);
106699580020Sratchov 			return 0;
106799580020Sratchov 		}
106899580020Sratchov 		if (!ctl_setval(c, ntohs(m->u.ctlset.val))) {
106999580020Sratchov #ifdef DEBUG
10707b639200Sratchov 			logx(1, "sock %d: CTLSET, bad value", f->fd);
1071d07fece6Sratchov #endif
1072d07fece6Sratchov 			sock_close(f);
1073d07fece6Sratchov 			return 0;
1074d07fece6Sratchov 		}
1075d07fece6Sratchov 		f->rtodo = sizeof(struct amsg);
1076d07fece6Sratchov 		f->rstate = SOCK_RMSG;
107787bc9f6aSratchov 		break;
107887bc9f6aSratchov 	case AMSG_AUTH:
107987bc9f6aSratchov #ifdef DEBUG
10807b639200Sratchov 		logx(3, "sock %d: AUTH message", f->fd);
108187bc9f6aSratchov #endif
108287bc9f6aSratchov 		if (f->pstate != SOCK_AUTH) {
108387bc9f6aSratchov #ifdef DEBUG
10847b639200Sratchov 			logx(1, "sock %d: AUTH, wrong state", f->fd);
108587bc9f6aSratchov #endif
108687bc9f6aSratchov 			sock_close(f);
108787bc9f6aSratchov 			return 0;
108887bc9f6aSratchov 		}
108987bc9f6aSratchov 		if (!sock_auth(f)) {
109087bc9f6aSratchov 			sock_close(f);
109187bc9f6aSratchov 			return 0;
109287bc9f6aSratchov 		}
109387bc9f6aSratchov 		f->rstate = SOCK_RMSG;
109487bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
109587bc9f6aSratchov 		break;
109687bc9f6aSratchov 	case AMSG_HELLO:
109787bc9f6aSratchov #ifdef DEBUG
10987b639200Sratchov 		logx(3, "sock %d: HELLO message", f->fd);
109987bc9f6aSratchov #endif
110087bc9f6aSratchov 		if (f->pstate != SOCK_HELLO) {
110187bc9f6aSratchov #ifdef DEBUG
11027b639200Sratchov 			logx(1, "sock %d: HELLO, wrong state", f->fd);
110387bc9f6aSratchov #endif
110487bc9f6aSratchov 			sock_close(f);
110587bc9f6aSratchov 			return 0;
110687bc9f6aSratchov 		}
110787bc9f6aSratchov 		if (!sock_hello(f)) {
110887bc9f6aSratchov 			sock_close(f);
110987bc9f6aSratchov 			return 0;
111087bc9f6aSratchov 		}
111187bc9f6aSratchov 		AMSG_INIT(m);
111287bc9f6aSratchov 		m->cmd = htonl(AMSG_ACK);
111387bc9f6aSratchov 		f->rstate = SOCK_RRET;
111487bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
111587bc9f6aSratchov 		break;
111687bc9f6aSratchov 	case AMSG_BYE:
111787bc9f6aSratchov #ifdef DEBUG
11187b639200Sratchov 		logx(3, "sock %d: BYE message", f->fd);
111987bc9f6aSratchov #endif
112087bc9f6aSratchov 		if (s != NULL && f->pstate != SOCK_INIT) {
112187bc9f6aSratchov #ifdef DEBUG
11227b639200Sratchov 			logx(1, "sock %d: BYE, wrong state", f->fd);
112387bc9f6aSratchov #endif
112487bc9f6aSratchov 		}
112587bc9f6aSratchov 		sock_close(f);
112687bc9f6aSratchov 		return 0;
112787bc9f6aSratchov 	default:
112887bc9f6aSratchov #ifdef DEBUG
11297b639200Sratchov 		logx(1, "sock %d: unknown command in message", f->fd);
113087bc9f6aSratchov #endif
113187bc9f6aSratchov 		sock_close(f);
113287bc9f6aSratchov 		return 0;
113387bc9f6aSratchov 	}
113487bc9f6aSratchov 	return 1;
113587bc9f6aSratchov }
113687bc9f6aSratchov 
113787bc9f6aSratchov /*
113887bc9f6aSratchov  * build a message in f->wmsg, return 1 on success and 0 if
113987bc9f6aSratchov  * there's nothing to do. Assume f->wstate is SOCK_WIDLE
114087bc9f6aSratchov  */
114187bc9f6aSratchov int
114287bc9f6aSratchov sock_buildmsg(struct sock *f)
114387bc9f6aSratchov {
114499580020Sratchov 	unsigned int size, type, mask;
1145d07fece6Sratchov 	struct amsg_ctl_desc *desc;
1146d07fece6Sratchov 	struct ctl *c, **pc;
114787bc9f6aSratchov 
114887bc9f6aSratchov 	/*
114987bc9f6aSratchov 	 * If pos changed (or initial tick), build a MOVE message.
115087bc9f6aSratchov 	 */
115187bc9f6aSratchov 	if (f->tickpending) {
115287bc9f6aSratchov #ifdef DEBUG
11537b639200Sratchov 		logx(4, "sock %d: building MOVE message, delta = %d", f->fd, f->slot->delta);
115487bc9f6aSratchov #endif
115587bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
115687bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_MOVE);
115787bc9f6aSratchov 		f->wmsg.u.ts.delta = htonl(f->slot->delta);
115887bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
115987bc9f6aSratchov 		f->wstate = SOCK_WMSG;
116087bc9f6aSratchov 		f->tickpending = 0;
116187bc9f6aSratchov 		/*
116287bc9f6aSratchov 		 * XXX: use tickpending as accumulator rather than
116387bc9f6aSratchov 		 * slot->delta
116487bc9f6aSratchov 		 */
116587bc9f6aSratchov 		f->slot->delta = 0;
116687bc9f6aSratchov 		return 1;
116787bc9f6aSratchov 	}
116887bc9f6aSratchov 
116987bc9f6aSratchov 	if (f->fillpending > 0) {
117087bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
117187bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_FLOWCTL);
117287bc9f6aSratchov 		f->wmsg.u.ts.delta = htonl(f->fillpending);
117387bc9f6aSratchov 		size = f->fillpending;
117487bc9f6aSratchov 		if (f->slot)
117587bc9f6aSratchov 			size *= f->slot->mix.bpf;
117687bc9f6aSratchov 		f->rmax += size;
117787bc9f6aSratchov #ifdef DEBUG
11787b639200Sratchov 		logx(4, "sock %d: building FLOWCTL message, "
11797b639200Sratchov 		    "count = %d, rmax -> %d", f->fd, f->fillpending, f->rmax);
118087bc9f6aSratchov #endif
118187bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
118287bc9f6aSratchov 		f->wstate = SOCK_WMSG;
118387bc9f6aSratchov 		f->fillpending = 0;
118487bc9f6aSratchov 		return 1;
118587bc9f6aSratchov 	}
118687bc9f6aSratchov 
118787bc9f6aSratchov 	/*
118887bc9f6aSratchov 	 * if volume changed build a SETVOL message
118987bc9f6aSratchov 	 */
119087bc9f6aSratchov 	if (f->pstate >= SOCK_START && f->slot->vol != f->lastvol) {
119187bc9f6aSratchov #ifdef DEBUG
11927b639200Sratchov 		logx(3, "sock %d: building SETVOL message, vol = %d", f->fd,
11937b639200Sratchov 		    f->slot->vol);
119487bc9f6aSratchov #endif
119587bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
119687bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_SETVOL);
119787bc9f6aSratchov 		f->wmsg.u.vol.ctl = htonl(f->slot->vol);
119887bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
119987bc9f6aSratchov 		f->wstate = SOCK_WMSG;
120087bc9f6aSratchov 		f->lastvol = f->slot->vol;
120187bc9f6aSratchov 		return 1;
120287bc9f6aSratchov 	}
120387bc9f6aSratchov 
120487bc9f6aSratchov 	if (f->midi != NULL && f->midi->obuf.used > 0) {
120587bc9f6aSratchov 		size = f->midi->obuf.used;
120687bc9f6aSratchov 		if (size > AMSG_DATAMAX)
120787bc9f6aSratchov 			size = AMSG_DATAMAX;
120887bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
120987bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_DATA);
121087bc9f6aSratchov 		f->wmsg.u.data.size = htonl(size);
121187bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
121287bc9f6aSratchov 		f->wstate = SOCK_WMSG;
121387bc9f6aSratchov 		return 1;
121487bc9f6aSratchov 	}
121587bc9f6aSratchov 
121687bc9f6aSratchov 	/*
121787bc9f6aSratchov 	 * If data available, build a DATA message.
121887bc9f6aSratchov 	 */
12191d0df325Sratchov 	if (f->slot != NULL && f->wmax > 0 && f->slot->sub.buf.used > 0) {
122087bc9f6aSratchov 		size = f->slot->sub.buf.used;
122187bc9f6aSratchov 		if (size > AMSG_DATAMAX)
122287bc9f6aSratchov 			size = AMSG_DATAMAX;
122387bc9f6aSratchov 		if (size > f->walign)
122487bc9f6aSratchov 			size = f->walign;
122587bc9f6aSratchov 		if (size > f->wmax)
122687bc9f6aSratchov 			size = f->wmax;
122787bc9f6aSratchov 		size -= size % f->slot->sub.bpf;
122887bc9f6aSratchov #ifdef DEBUG
122987bc9f6aSratchov 		if (size == 0) {
12307b639200Sratchov 			logx(0, "sock %d: sock_buildmsg size == 0", f->fd);
123187bc9f6aSratchov 			panic();
123287bc9f6aSratchov 		}
123387bc9f6aSratchov #endif
123487bc9f6aSratchov 		f->walign -= size;
123587bc9f6aSratchov 		f->wmax -= size;
123687bc9f6aSratchov 		if (f->walign == 0)
123787bc9f6aSratchov 			f->walign = f->slot->round * f->slot->sub.bpf;
123887bc9f6aSratchov #ifdef DEBUG
12397b639200Sratchov 		logx(4, "sock %d: building audio DATA message, size = %d", f->fd, size);
124087bc9f6aSratchov #endif
124187bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
124287bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_DATA);
124387bc9f6aSratchov 		f->wmsg.u.data.size = htonl(size);
124487bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
124587bc9f6aSratchov 		f->wstate = SOCK_WMSG;
124687bc9f6aSratchov 		return 1;
124787bc9f6aSratchov 	}
124887bc9f6aSratchov 
124987bc9f6aSratchov 	if (f->stoppending) {
125087bc9f6aSratchov #ifdef DEBUG
12517b639200Sratchov 		logx(3, "sock %d: building STOP message", f->fd);
125287bc9f6aSratchov #endif
125387bc9f6aSratchov 		f->stoppending = 0;
125487bc9f6aSratchov 		f->pstate = SOCK_INIT;
125587bc9f6aSratchov 		AMSG_INIT(&f->wmsg);
125687bc9f6aSratchov 		f->wmsg.cmd = htonl(AMSG_STOP);
125787bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
125887bc9f6aSratchov 		f->wstate = SOCK_WMSG;
125987bc9f6aSratchov 		return 1;
126087bc9f6aSratchov 	}
1261d07fece6Sratchov 
1262d07fece6Sratchov 	/*
1263d07fece6Sratchov 	 * XXX: add a flag indicating if there are changes
1264d07fece6Sratchov 	 * in controls not seen by this client, rather
1265d07fece6Sratchov 	 * than walking through the full list of control
1266d07fece6Sratchov 	 * searching for the {desc,val}_mask bits
1267d07fece6Sratchov 	 */
1268d07fece6Sratchov 	if (f->ctlslot && (f->ctlops & SOCK_CTLDESC)) {
1269c7054416Sratchov 		mask = f->ctlslot->self;
1270d07fece6Sratchov 		size = 0;
127199580020Sratchov 		pc = &ctl_list;
1272d07fece6Sratchov 		while ((c = *pc) != NULL) {
1273d07fece6Sratchov 			if ((c->desc_mask & mask) == 0 ||
1274d07fece6Sratchov 			    (c->refs_mask & mask) == 0) {
1275d07fece6Sratchov 				pc = &c->next;
1276d07fece6Sratchov 				continue;
1277d07fece6Sratchov 			}
12784d8b188fSratchov 			if (size + f->ctl_desc_size > SOCK_CTLDESC_SIZE)
1279d07fece6Sratchov 				break;
12803e5ee6d4Sratchov 			desc = (struct amsg_ctl_desc *)(f->ctldesc + size);
1281d07fece6Sratchov 			c->desc_mask &= ~mask;
1282d07fece6Sratchov 			c->val_mask &= ~mask;
128399580020Sratchov 			type = ctlslot_visible(f->ctlslot, c) ?
128499580020Sratchov 			    c->type : CTL_NONE;
128536355b88Sratchov 			strlcpy(desc->group, ctlgroup(f, c), AMSG_CTL_NAMEMAX);
1286d07fece6Sratchov 			strlcpy(desc->node0.name, c->node0.name,
1287d07fece6Sratchov 			    AMSG_CTL_NAMEMAX);
1288d07fece6Sratchov 			desc->node0.unit = ntohs(c->node0.unit);
1289d07fece6Sratchov 			strlcpy(desc->node1.name, c->node1.name,
1290d07fece6Sratchov 			    AMSG_CTL_NAMEMAX);
1291d07fece6Sratchov 			desc->node1.unit = ntohs(c->node1.unit);
129299580020Sratchov 			desc->type = type;
1293d07fece6Sratchov 			strlcpy(desc->func, c->func, AMSG_CTL_NAMEMAX);
1294d07fece6Sratchov 			desc->addr = htons(c->addr);
1295d07fece6Sratchov 			desc->maxval = htons(c->maxval);
1296d07fece6Sratchov 			desc->curval = htons(c->curval);
12974d8b188fSratchov 
12984d8b188fSratchov 			/* old clients don't have the 'display' member */
12994d8b188fSratchov 			if (f->ctl_desc_size >= offsetof(struct amsg_ctl_desc,
13004d8b188fSratchov 				display) + AMSG_CTL_DISPLAYMAX) {
13014d8b188fSratchov 				strlcpy(desc->display, c->display, AMSG_CTL_DISPLAYMAX);
13024d8b188fSratchov 			}
13034d8b188fSratchov 
13044d8b188fSratchov 			size += f->ctl_desc_size;
1305d07fece6Sratchov 
1306d07fece6Sratchov 			/* if this is a deleted entry unref it */
130799580020Sratchov 			if (type == CTL_NONE) {
1308d07fece6Sratchov 				c->refs_mask &= ~mask;
1309d07fece6Sratchov 				if (c->refs_mask == 0) {
1310d07fece6Sratchov 					*pc = c->next;
1311d07fece6Sratchov 					xfree(c);
1312d07fece6Sratchov 					continue;
1313d07fece6Sratchov 				}
1314d07fece6Sratchov 			}
1315d07fece6Sratchov 
1316d07fece6Sratchov 			pc = &c->next;
1317d07fece6Sratchov 		}
1318d07fece6Sratchov 		if (size > 0) {
1319d07fece6Sratchov 			AMSG_INIT(&f->wmsg);
1320d07fece6Sratchov 			f->wmsg.cmd = htonl(AMSG_DATA);
1321d07fece6Sratchov 			f->wmsg.u.data.size = htonl(size);
1322d07fece6Sratchov 			f->wtodo = sizeof(struct amsg);
1323d07fece6Sratchov 			f->wstate = SOCK_WMSG;
1324d07fece6Sratchov #ifdef DEBUG
13257b639200Sratchov 			logx(3, "sock %d: building control DATA message", f->fd);
1326d07fece6Sratchov #endif
1327d07fece6Sratchov 			return 1;
1328d07fece6Sratchov 		}
1329d07fece6Sratchov 	}
1330d07fece6Sratchov 	if (f->ctlslot && (f->ctlops & SOCK_CTLVAL)) {
1331c7054416Sratchov 		mask = f->ctlslot->self;
133299580020Sratchov 		for (c = ctl_list; c != NULL; c = c->next) {
133399580020Sratchov 			if (!ctlslot_visible(f->ctlslot, c))
133499580020Sratchov 				continue;
1335d07fece6Sratchov 			if ((c->val_mask & mask) == 0)
1336d07fece6Sratchov 				continue;
1337d07fece6Sratchov 			c->val_mask &= ~mask;
1338d07fece6Sratchov 			AMSG_INIT(&f->wmsg);
1339d07fece6Sratchov 			f->wmsg.cmd = htonl(AMSG_CTLSET);
1340d07fece6Sratchov 			f->wmsg.u.ctlset.addr = htons(c->addr);
1341d07fece6Sratchov 			f->wmsg.u.ctlset.val = htons(c->curval);
1342d07fece6Sratchov 			f->wtodo = sizeof(struct amsg);
1343d07fece6Sratchov 			f->wstate = SOCK_WMSG;
1344d07fece6Sratchov #ifdef DEBUG
13457b639200Sratchov 			logx(3, "sock %d: building CTLSET message", f->fd);
1346d07fece6Sratchov #endif
1347d07fece6Sratchov 			return 1;
1348d07fece6Sratchov 		}
1349d07fece6Sratchov 	}
1350d07fece6Sratchov 	if (f->ctlslot && f->ctlsyncpending) {
1351d07fece6Sratchov 		f->ctlsyncpending = 0;
1352d07fece6Sratchov 		f->wmsg.cmd = htonl(AMSG_CTLSYNC);
1353d07fece6Sratchov 		f->wtodo = sizeof(struct amsg);
1354d07fece6Sratchov 		f->wstate = SOCK_WMSG;
1355d07fece6Sratchov #ifdef DEBUG
13567b639200Sratchov 		logx(3, "sock %d: building CTLSYNC message", f->fd);
1357d07fece6Sratchov #endif
1358d07fece6Sratchov 		return 1;
1359d07fece6Sratchov 	}
136087bc9f6aSratchov #ifdef DEBUG
13617b639200Sratchov 	logx(4, "sock %d: no messages to build anymore, idling...", f->fd);
136287bc9f6aSratchov #endif
136387bc9f6aSratchov 	f->wstate = SOCK_WIDLE;
136487bc9f6aSratchov 	return 0;
136587bc9f6aSratchov }
136687bc9f6aSratchov 
136787bc9f6aSratchov /*
136887bc9f6aSratchov  * iteration of the socket reader loop, return 1 on success
136987bc9f6aSratchov  */
137087bc9f6aSratchov int
137187bc9f6aSratchov sock_read(struct sock *f)
137287bc9f6aSratchov {
137387bc9f6aSratchov #ifdef DEBUG
13747b639200Sratchov 	logx(4, "sock %d: reading %u todo", f->fd, f->rtodo);
137587bc9f6aSratchov #endif
137687bc9f6aSratchov 	switch (f->rstate) {
137787bc9f6aSratchov 	case SOCK_RIDLE:
137887bc9f6aSratchov 		return 0;
137987bc9f6aSratchov 	case SOCK_RMSG:
138087bc9f6aSratchov 		if (!sock_rmsg(f))
138187bc9f6aSratchov 			return 0;
138287bc9f6aSratchov 		if (!sock_execmsg(f))
138387bc9f6aSratchov 			return 0;
138487bc9f6aSratchov 		break;
138587bc9f6aSratchov 	case SOCK_RDATA:
138687bc9f6aSratchov 		if (!sock_rdata(f))
138787bc9f6aSratchov 			return 0;
138887bc9f6aSratchov 		f->rstate = SOCK_RMSG;
138987bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
139087bc9f6aSratchov 		break;
139187bc9f6aSratchov 	case SOCK_RRET:
139287bc9f6aSratchov 		if (f->wstate != SOCK_WIDLE) {
139387bc9f6aSratchov #ifdef DEBUG
13947b639200Sratchov 			logx(4, "sock %d: can't reply, write-end blocked", f->fd);
139587bc9f6aSratchov #endif
139687bc9f6aSratchov 			return 0;
139787bc9f6aSratchov 		}
139887bc9f6aSratchov 		f->wmsg = f->rmsg;
139987bc9f6aSratchov 		f->wstate = SOCK_WMSG;
140087bc9f6aSratchov 		f->wtodo = sizeof(struct amsg);
140187bc9f6aSratchov 		f->rstate = SOCK_RMSG;
140287bc9f6aSratchov 		f->rtodo = sizeof(struct amsg);
140387bc9f6aSratchov #ifdef DEBUG
14047b639200Sratchov 		logx(4, "sock %d: copied RRET message", f->fd);
140587bc9f6aSratchov #endif
140687bc9f6aSratchov 	}
140787bc9f6aSratchov 	return 1;
140887bc9f6aSratchov }
140987bc9f6aSratchov 
141087bc9f6aSratchov /*
141187bc9f6aSratchov  * iteration of the socket writer loop, return 1 on success
141287bc9f6aSratchov  */
141387bc9f6aSratchov int
141487bc9f6aSratchov sock_write(struct sock *f)
141587bc9f6aSratchov {
141687bc9f6aSratchov #ifdef DEBUG
14177b639200Sratchov 	logx(4, "sock %d: writing", f->fd);
141887bc9f6aSratchov #endif
141987bc9f6aSratchov 	switch (f->wstate) {
142087bc9f6aSratchov 	case SOCK_WMSG:
142187bc9f6aSratchov 		if (!sock_wmsg(f))
142287bc9f6aSratchov 			return 0;
1423f2c5b8a8Sratchov 		/*
1424f2c5b8a8Sratchov 		 * f->wmsg is either build by sock_buildmsg() or
1425f2c5b8a8Sratchov 		 * copied from f->rmsg (in the SOCK_RRET state), so
1426f2c5b8a8Sratchov 		 * it's safe.
1427f2c5b8a8Sratchov 		 */
142887bc9f6aSratchov 		if (ntohl(f->wmsg.cmd) != AMSG_DATA) {
142987bc9f6aSratchov 			f->wstate = SOCK_WIDLE;
143087bc9f6aSratchov 			f->wtodo = 0xdeadbeef;
143187bc9f6aSratchov 			break;
143287bc9f6aSratchov 		}
143387bc9f6aSratchov 		f->wstate = SOCK_WDATA;
143487bc9f6aSratchov 		f->wsize = f->wtodo = ntohl(f->wmsg.u.data.size);
1435591be731Sjsg 		/* FALLTHROUGH */
143687bc9f6aSratchov 	case SOCK_WDATA:
143787bc9f6aSratchov 		if (!sock_wdata(f))
143887bc9f6aSratchov 			return 0;
143987bc9f6aSratchov 		if (f->wtodo > 0)
144087bc9f6aSratchov 			break;
144187bc9f6aSratchov 		f->wstate = SOCK_WIDLE;
144287bc9f6aSratchov 		f->wtodo = 0xdeadbeef;
144387bc9f6aSratchov 		if (f->pstate == SOCK_STOP) {
144487bc9f6aSratchov 			f->pstate = SOCK_INIT;
144587bc9f6aSratchov 			f->wmax = 0;
144687bc9f6aSratchov #ifdef DEBUG
14477b639200Sratchov 			logx(4, "sock %d: drained, moved to INIT state", f->fd);
144887bc9f6aSratchov #endif
144987bc9f6aSratchov 		}
1450591be731Sjsg 		/* FALLTHROUGH */
145187bc9f6aSratchov 	case SOCK_WIDLE:
145287bc9f6aSratchov 		if (f->rstate == SOCK_RRET) {
145387bc9f6aSratchov 			f->wmsg = f->rmsg;
145487bc9f6aSratchov 			f->wstate = SOCK_WMSG;
145587bc9f6aSratchov 			f->wtodo = sizeof(struct amsg);
145687bc9f6aSratchov 			f->rstate = SOCK_RMSG;
145787bc9f6aSratchov 			f->rtodo = sizeof(struct amsg);
145887bc9f6aSratchov #ifdef DEBUG
14597b639200Sratchov 			logx(4, "sock %d: copied RRET message", f->fd);
146087bc9f6aSratchov #endif
146187bc9f6aSratchov 		} else {
146287bc9f6aSratchov 			if (!sock_buildmsg(f))
146387bc9f6aSratchov 				return 0;
146487bc9f6aSratchov 		}
146587bc9f6aSratchov 		break;
146687bc9f6aSratchov #ifdef DEBUG
146787bc9f6aSratchov 	default:
14687b639200Sratchov 		logx(0, "sock %d: bad writing end state", f->fd);
146987bc9f6aSratchov 		panic();
147087bc9f6aSratchov #endif
147187bc9f6aSratchov 	}
147287bc9f6aSratchov 	return 1;
147387bc9f6aSratchov }
147487bc9f6aSratchov 
147587bc9f6aSratchov int
147687bc9f6aSratchov sock_pollfd(void *arg, struct pollfd *pfd)
147787bc9f6aSratchov {
147887bc9f6aSratchov 	struct sock *f = arg;
147987bc9f6aSratchov 	int events = 0;
148087bc9f6aSratchov 
148187bc9f6aSratchov 	/*
148287bc9f6aSratchov 	 * feedback counters, clock ticks and alike may have changed,
148387bc9f6aSratchov 	 * prepare a message to trigger writes
148487bc9f6aSratchov 	 *
148587bc9f6aSratchov 	 * XXX: doing this at the beginning of the cycle is not optimal,
148687bc9f6aSratchov 	 * because state is changed at the end of the read cycle, and
148787bc9f6aSratchov 	 * thus counters, ret message and alike are generated then.
148887bc9f6aSratchov 	 */
148987bc9f6aSratchov 	if (f->wstate == SOCK_WIDLE && f->rstate != SOCK_RRET)
149087bc9f6aSratchov 		sock_buildmsg(f);
149187bc9f6aSratchov 
149287bc9f6aSratchov 	if (f->rstate == SOCK_RMSG ||
149387bc9f6aSratchov 	    f->rstate == SOCK_RDATA)
149487bc9f6aSratchov 		events |= POLLIN;
149587bc9f6aSratchov 	if (f->rstate == SOCK_RRET ||
149687bc9f6aSratchov 	    f->wstate == SOCK_WMSG ||
149787bc9f6aSratchov 	    f->wstate == SOCK_WDATA)
149887bc9f6aSratchov 		events |= POLLOUT;
149987bc9f6aSratchov 	pfd->fd = f->fd;
150087bc9f6aSratchov 	pfd->events = events;
150187bc9f6aSratchov 	return 1;
150287bc9f6aSratchov }
150387bc9f6aSratchov 
150487bc9f6aSratchov int
150587bc9f6aSratchov sock_revents(void *arg, struct pollfd *pfd)
150687bc9f6aSratchov {
150787bc9f6aSratchov 	return pfd->revents;
150887bc9f6aSratchov }
150987bc9f6aSratchov 
151087bc9f6aSratchov void
151187bc9f6aSratchov sock_in(void *arg)
151287bc9f6aSratchov {
151387bc9f6aSratchov 	struct sock *f = arg;
151487bc9f6aSratchov 
151587bc9f6aSratchov 	while (sock_read(f))
151687bc9f6aSratchov 		;
151787bc9f6aSratchov }
151887bc9f6aSratchov 
151987bc9f6aSratchov void
152087bc9f6aSratchov sock_out(void *arg)
152187bc9f6aSratchov {
152287bc9f6aSratchov 	struct sock *f = arg;
152387bc9f6aSratchov 
152487bc9f6aSratchov 	while (sock_write(f))
152587bc9f6aSratchov 		;
152687bc9f6aSratchov }
152787bc9f6aSratchov 
152887bc9f6aSratchov void
152987bc9f6aSratchov sock_hup(void *arg)
153087bc9f6aSratchov {
153187bc9f6aSratchov 	struct sock *f = arg;
153287bc9f6aSratchov 
153387bc9f6aSratchov 	sock_close(f);
153487bc9f6aSratchov }
1535