xref: /openbsd-src/lib/libsndio/sioctl_aucat.c (revision 4d8b188f3074948b01f60b8d4fd246ea279f07b8)
1d07fece6Sratchov /*
2d07fece6Sratchov  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
3d07fece6Sratchov  *
4d07fece6Sratchov  * Permission to use, copy, modify, and distribute this software for any
5d07fece6Sratchov  * purpose with or without fee is hereby granted, provided that the above
6d07fece6Sratchov  * copyright notice and this permission notice appear in all copies.
7d07fece6Sratchov  *
8d07fece6Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9d07fece6Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10d07fece6Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11d07fece6Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12d07fece6Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13d07fece6Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14d07fece6Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15d07fece6Sratchov  */
16d07fece6Sratchov #include <errno.h>
17d07fece6Sratchov #include <poll.h>
18d07fece6Sratchov #include <stdio.h>
19d07fece6Sratchov #include <stdlib.h>
20d07fece6Sratchov #include <string.h>
21d07fece6Sratchov #include <sndio.h>
22d07fece6Sratchov #include <unistd.h>
23d07fece6Sratchov #include <arpa/inet.h>
24d07fece6Sratchov #include "debug.h"
25d07fece6Sratchov #include "aucat.h"
26d07fece6Sratchov #include "sioctl_priv.h"
27d07fece6Sratchov 
28d07fece6Sratchov struct sioctl_aucat_hdl {
29d07fece6Sratchov 	struct sioctl_hdl sioctl;
30d07fece6Sratchov 	struct aucat aucat;
31d07fece6Sratchov 	struct sioctl_desc desc;
32d07fece6Sratchov 	struct amsg_ctl_desc buf[16];
33d07fece6Sratchov 	size_t buf_wpos;
34d07fece6Sratchov 	int dump_wait;
35d07fece6Sratchov };
36d07fece6Sratchov 
37d07fece6Sratchov static void sioctl_aucat_close(struct sioctl_hdl *);
38d07fece6Sratchov static int sioctl_aucat_nfds(struct sioctl_hdl *);
39d07fece6Sratchov static int sioctl_aucat_pollfd(struct sioctl_hdl *, struct pollfd *, int);
40d07fece6Sratchov static int sioctl_aucat_revents(struct sioctl_hdl *, struct pollfd *);
41d07fece6Sratchov static int sioctl_aucat_setctl(struct sioctl_hdl *, unsigned int, unsigned int);
42d07fece6Sratchov static int sioctl_aucat_onval(struct sioctl_hdl *);
43d07fece6Sratchov static int sioctl_aucat_ondesc(struct sioctl_hdl *);
44d07fece6Sratchov 
45d07fece6Sratchov /*
46d07fece6Sratchov  * operations every device should support
47d07fece6Sratchov  */
48d07fece6Sratchov struct sioctl_ops sioctl_aucat_ops = {
49d07fece6Sratchov 	sioctl_aucat_close,
50d07fece6Sratchov 	sioctl_aucat_nfds,
51d07fece6Sratchov 	sioctl_aucat_pollfd,
52d07fece6Sratchov 	sioctl_aucat_revents,
53d07fece6Sratchov 	sioctl_aucat_setctl,
54d07fece6Sratchov 	sioctl_aucat_onval,
55d07fece6Sratchov 	sioctl_aucat_ondesc
56d07fece6Sratchov };
57d07fece6Sratchov 
58d07fece6Sratchov static int
sioctl_aucat_rdata(struct sioctl_aucat_hdl * hdl)59d07fece6Sratchov sioctl_aucat_rdata(struct sioctl_aucat_hdl *hdl)
60d07fece6Sratchov {
61d07fece6Sratchov 	struct sioctl_desc desc;
62d07fece6Sratchov 	struct amsg_ctl_desc *c;
63d07fece6Sratchov 	size_t rpos;
64d07fece6Sratchov 	int n;
65d07fece6Sratchov 
66d07fece6Sratchov 	while (hdl->aucat.rstate == RSTATE_DATA) {
67d07fece6Sratchov 
68d07fece6Sratchov 		/* read entries */
69d07fece6Sratchov 		while (hdl->buf_wpos < sizeof(hdl->buf) &&
70d07fece6Sratchov 		    hdl->aucat.rstate == RSTATE_DATA) {
71d07fece6Sratchov 			n = _aucat_rdata(&hdl->aucat,
72d07fece6Sratchov 			    (unsigned char *)hdl->buf + hdl->buf_wpos,
73d07fece6Sratchov 			    sizeof(hdl->buf) - hdl->buf_wpos,
74d07fece6Sratchov 			    &hdl->sioctl.eof);
75d07fece6Sratchov 			if (n == 0 || hdl->sioctl.eof)
76d07fece6Sratchov 				return 0;
77d07fece6Sratchov 			hdl->buf_wpos += n;
78d07fece6Sratchov 		}
79d07fece6Sratchov 
80d07fece6Sratchov 		/* parse entries */
81d07fece6Sratchov 		c = hdl->buf;
82d07fece6Sratchov 		rpos = 0;
83d07fece6Sratchov 		while (rpos < hdl->buf_wpos) {
84d07fece6Sratchov 			strlcpy(desc.group, c->group, SIOCTL_NAMEMAX);
85d07fece6Sratchov 			strlcpy(desc.node0.name, c->node0.name, SIOCTL_NAMEMAX);
86d07fece6Sratchov 			desc.node0.unit = (int16_t)ntohs(c->node0.unit);
87d07fece6Sratchov 			strlcpy(desc.node1.name, c->node1.name, SIOCTL_NAMEMAX);
88d07fece6Sratchov 			desc.node1.unit = (int16_t)ntohs(c->node1.unit);
89d07fece6Sratchov 			strlcpy(desc.func, c->func, SIOCTL_NAMEMAX);
90*4d8b188fSratchov 			strlcpy(desc.display, c->display, SIOCTL_DISPLAYMAX);
91d07fece6Sratchov 			desc.type = c->type;
92d07fece6Sratchov 			desc.addr = ntohs(c->addr);
93d07fece6Sratchov 			desc.maxval = ntohs(c->maxval);
94d07fece6Sratchov 			_sioctl_ondesc_cb(&hdl->sioctl,
95d07fece6Sratchov 			    &desc, ntohs(c->curval));
96d07fece6Sratchov 			rpos += sizeof(struct amsg_ctl_desc);
97d07fece6Sratchov 			c++;
98d07fece6Sratchov 		}
99d07fece6Sratchov 		hdl->buf_wpos = 0;
100d07fece6Sratchov 	}
101d07fece6Sratchov 	return 1;
102d07fece6Sratchov }
103d07fece6Sratchov 
104d07fece6Sratchov /*
105d07fece6Sratchov  * execute the next message, return 0 if blocked
106d07fece6Sratchov  */
107d07fece6Sratchov static int
sioctl_aucat_runmsg(struct sioctl_aucat_hdl * hdl)108d07fece6Sratchov sioctl_aucat_runmsg(struct sioctl_aucat_hdl *hdl)
109d07fece6Sratchov {
110d07fece6Sratchov 	if (!_aucat_rmsg(&hdl->aucat, &hdl->sioctl.eof))
111d07fece6Sratchov 		return 0;
112d07fece6Sratchov 	switch (ntohl(hdl->aucat.rmsg.cmd)) {
113d07fece6Sratchov 	case AMSG_DATA:
114d07fece6Sratchov 		hdl->buf_wpos = 0;
115d07fece6Sratchov 		if (!sioctl_aucat_rdata(hdl))
116d07fece6Sratchov 			return 0;
117d07fece6Sratchov 		break;
118d07fece6Sratchov 	case AMSG_CTLSET:
119d07fece6Sratchov 		DPRINTF("sioctl_aucat_runmsg: got CTLSET\n");
120d07fece6Sratchov 		_sioctl_onval_cb(&hdl->sioctl,
121d07fece6Sratchov 		    ntohs(hdl->aucat.rmsg.u.ctlset.addr),
122d07fece6Sratchov 		    ntohs(hdl->aucat.rmsg.u.ctlset.val));
123d07fece6Sratchov 		break;
124d07fece6Sratchov 	case AMSG_CTLSYNC:
125d07fece6Sratchov 		DPRINTF("sioctl_aucat_runmsg: got CTLSYNC\n");
126d07fece6Sratchov 		hdl->dump_wait = 0;
127d07fece6Sratchov 		_sioctl_ondesc_cb(&hdl->sioctl, NULL, 0);
128d07fece6Sratchov 		break;
129d07fece6Sratchov 	default:
130d07fece6Sratchov 		DPRINTF("sio_aucat_runmsg: unhandled message %u\n",
131d07fece6Sratchov 		    hdl->aucat.rmsg.cmd);
132d07fece6Sratchov 		hdl->sioctl.eof = 1;
133d07fece6Sratchov 		return 0;
134d07fece6Sratchov 	}
135d07fece6Sratchov 	hdl->aucat.rstate = RSTATE_MSG;
136d07fece6Sratchov 	hdl->aucat.rtodo = sizeof(struct amsg);
137d07fece6Sratchov 	return 1;
138d07fece6Sratchov }
139d07fece6Sratchov 
140d07fece6Sratchov struct sioctl_hdl *
_sioctl_aucat_open(const char * str,unsigned int mode,int nbio)141d07fece6Sratchov _sioctl_aucat_open(const char *str, unsigned int mode, int nbio)
142d07fece6Sratchov {
143d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl;
144d07fece6Sratchov 
145d07fece6Sratchov 	hdl = malloc(sizeof(struct sioctl_aucat_hdl));
146d07fece6Sratchov 	if (hdl == NULL)
147d07fece6Sratchov 		return NULL;
148d07fece6Sratchov 	if (!_aucat_open(&hdl->aucat, str, mode))
149d07fece6Sratchov 		goto bad;
150d07fece6Sratchov 	_sioctl_create(&hdl->sioctl, &sioctl_aucat_ops, mode, nbio);
151d07fece6Sratchov 	if (!_aucat_setfl(&hdl->aucat, 1, &hdl->sioctl.eof))
152d07fece6Sratchov 		goto bad;
153d07fece6Sratchov 	hdl->dump_wait = 0;
154d07fece6Sratchov 	return (struct sioctl_hdl *)hdl;
155d07fece6Sratchov bad:
156d07fece6Sratchov 	free(hdl);
157d07fece6Sratchov 	return NULL;
158d07fece6Sratchov }
159d07fece6Sratchov 
160d07fece6Sratchov static void
sioctl_aucat_close(struct sioctl_hdl * addr)161d07fece6Sratchov sioctl_aucat_close(struct sioctl_hdl *addr)
162d07fece6Sratchov {
163d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
164d07fece6Sratchov 
165d07fece6Sratchov 	if (!hdl->sioctl.eof)
166d07fece6Sratchov 		_aucat_setfl(&hdl->aucat, 0, &hdl->sioctl.eof);
167d07fece6Sratchov 	_aucat_close(&hdl->aucat, hdl->sioctl.eof);
168d07fece6Sratchov 	free(hdl);
169d07fece6Sratchov }
170d07fece6Sratchov 
171d07fece6Sratchov static int
sioctl_aucat_ondesc(struct sioctl_hdl * addr)172d07fece6Sratchov sioctl_aucat_ondesc(struct sioctl_hdl *addr)
173d07fece6Sratchov {
174d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
175d07fece6Sratchov 
176d07fece6Sratchov 	while (hdl->aucat.wstate != WSTATE_IDLE) {
177d07fece6Sratchov 		if (!_sioctl_psleep(&hdl->sioctl, POLLOUT))
178d07fece6Sratchov 			return 0;
179d07fece6Sratchov 	}
180d07fece6Sratchov 	AMSG_INIT(&hdl->aucat.wmsg);
181d07fece6Sratchov 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB);
182d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlsub.desc = 1;
183d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlsub.val = 0;
184d07fece6Sratchov 	hdl->aucat.wtodo = sizeof(struct amsg);
185d07fece6Sratchov 	if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
186d07fece6Sratchov 		return 0;
187d07fece6Sratchov 	hdl->dump_wait = 1;
188d07fece6Sratchov 	while (hdl->dump_wait) {
189d07fece6Sratchov 		DPRINTF("psleeping...\n");
190d07fece6Sratchov 		if (!_sioctl_psleep(&hdl->sioctl, 0))
191d07fece6Sratchov 			return 0;
192d07fece6Sratchov 		DPRINTF("psleeping done\n");
193d07fece6Sratchov 	}
194d07fece6Sratchov 	DPRINTF("done\n");
195d07fece6Sratchov 	return 1;
196d07fece6Sratchov }
197d07fece6Sratchov 
198d07fece6Sratchov static int
sioctl_aucat_onval(struct sioctl_hdl * addr)199d07fece6Sratchov sioctl_aucat_onval(struct sioctl_hdl *addr)
200d07fece6Sratchov {
201d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
202d07fece6Sratchov 
203d07fece6Sratchov 	while (hdl->aucat.wstate != WSTATE_IDLE) {
204d07fece6Sratchov 		if (!_sioctl_psleep(&hdl->sioctl, POLLOUT))
205d07fece6Sratchov 			return 0;
206d07fece6Sratchov 	}
207d07fece6Sratchov 	AMSG_INIT(&hdl->aucat.wmsg);
208d07fece6Sratchov 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB);
209d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlsub.desc = 1;
210d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlsub.val = 1;
211d07fece6Sratchov 	hdl->aucat.wtodo = sizeof(struct amsg);
212d07fece6Sratchov 	if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
213d07fece6Sratchov 		return 0;
214d07fece6Sratchov 	return 1;
215d07fece6Sratchov }
216d07fece6Sratchov 
217d07fece6Sratchov static int
sioctl_aucat_setctl(struct sioctl_hdl * addr,unsigned int a,unsigned int v)218d07fece6Sratchov sioctl_aucat_setctl(struct sioctl_hdl *addr, unsigned int a, unsigned int v)
219d07fece6Sratchov {
220d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
221d07fece6Sratchov 
222d07fece6Sratchov 	hdl->aucat.wstate = WSTATE_MSG;
223d07fece6Sratchov 	hdl->aucat.wtodo = sizeof(struct amsg);
224d07fece6Sratchov 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSET);
225d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlset.addr = htons(a);
226d07fece6Sratchov 	hdl->aucat.wmsg.u.ctlset.val = htons(v);
227d07fece6Sratchov 	while (hdl->aucat.wstate != WSTATE_IDLE) {
228d07fece6Sratchov 		if (_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
229d07fece6Sratchov 			break;
230d07fece6Sratchov 		if (hdl->sioctl.nbio || !_sioctl_psleep(&hdl->sioctl, POLLOUT))
231d07fece6Sratchov 			return 0;
232d07fece6Sratchov 	}
233d07fece6Sratchov 	return 1;
234d07fece6Sratchov }
235d07fece6Sratchov 
236d07fece6Sratchov static int
sioctl_aucat_nfds(struct sioctl_hdl * addr)237d07fece6Sratchov sioctl_aucat_nfds(struct sioctl_hdl *addr)
238d07fece6Sratchov {
239d07fece6Sratchov 	return 1;
240d07fece6Sratchov }
241d07fece6Sratchov 
242d07fece6Sratchov static int
sioctl_aucat_pollfd(struct sioctl_hdl * addr,struct pollfd * pfd,int events)243d07fece6Sratchov sioctl_aucat_pollfd(struct sioctl_hdl *addr, struct pollfd *pfd, int events)
244d07fece6Sratchov {
245d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
246d07fece6Sratchov 
247d07fece6Sratchov 	return _aucat_pollfd(&hdl->aucat, pfd, events | POLLIN);
248d07fece6Sratchov }
249d07fece6Sratchov 
250d07fece6Sratchov static int
sioctl_aucat_revents(struct sioctl_hdl * addr,struct pollfd * pfd)251d07fece6Sratchov sioctl_aucat_revents(struct sioctl_hdl *addr, struct pollfd *pfd)
252d07fece6Sratchov {
253d07fece6Sratchov 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
254d07fece6Sratchov 	int revents;
255d07fece6Sratchov 
256d07fece6Sratchov 	revents = _aucat_revents(&hdl->aucat, pfd);
257d07fece6Sratchov 	if (revents & POLLIN) {
258d07fece6Sratchov 		while (1) {
259d07fece6Sratchov 			if (hdl->aucat.rstate == RSTATE_MSG) {
260d07fece6Sratchov 				if (!sioctl_aucat_runmsg(hdl))
261d07fece6Sratchov 					break;
262d07fece6Sratchov 			}
263d07fece6Sratchov 			if (hdl->aucat.rstate == RSTATE_DATA) {
264d07fece6Sratchov 				if (!sioctl_aucat_rdata(hdl))
265d07fece6Sratchov 					break;
266d07fece6Sratchov 			}
267d07fece6Sratchov 		}
268d07fece6Sratchov 		revents &= ~POLLIN;
269d07fece6Sratchov 	}
270d07fece6Sratchov 	if (hdl->sioctl.eof)
271d07fece6Sratchov 		return POLLHUP;
272d07fece6Sratchov 	DPRINTFN(3, "sioctl_aucat_revents: revents = 0x%x\n", revents);
273d07fece6Sratchov 	return revents;
274d07fece6Sratchov }
275