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